|
1 | 1 | package jooby; |
2 | 2 |
|
| 3 | +import static java.util.Objects.requireNonNull; |
| 4 | + |
| 5 | +import java.util.Arrays; |
| 6 | +import java.util.Collections; |
3 | 7 | import java.util.List; |
| 8 | +import java.util.Optional; |
| 9 | +import java.util.concurrent.atomic.AtomicInteger; |
4 | 10 |
|
5 | 11 | import javax.annotation.Nonnull; |
6 | 12 |
|
7 | | -import jooby.internal.RouteDefinitionImpl; |
| 13 | +import jooby.internal.RouteImpl; |
| 14 | +import jooby.internal.RouteMatcher; |
| 15 | +import jooby.internal.RoutePattern; |
8 | 16 |
|
9 | 17 | import com.google.common.annotations.Beta; |
| 18 | +import com.google.common.collect.ImmutableList; |
| 19 | +import com.google.common.collect.Lists; |
10 | 20 |
|
11 | 21 | /** |
12 | 22 | * DSL for customize routes. |
|
62 | 72 | * @since 0.1.0 |
63 | 73 | */ |
64 | 74 | @Beta |
65 | | -public interface RouteDefinition { |
| 75 | +public class RouteDefinition { |
66 | 76 |
|
67 | | - public class Builder { |
| 77 | + private static final List<MediaType> ALL = ImmutableList.of(MediaType.all); |
| 78 | + private static final AtomicInteger INDEX = new AtomicInteger(-1); |
68 | 79 |
|
69 | | - public static RouteDefinition newRoute(final String verb, final String path, |
70 | | - final Router router) { |
71 | | - return new RouteDefinitionImpl(verb, path, router); |
72 | | - } |
| 80 | + private final int index = INDEX.incrementAndGet(); |
73 | 81 |
|
74 | | - public static RouteDefinition newRoute(final String verb, final String path, |
75 | | - final Filter filter) { |
76 | | - return new RouteDefinitionImpl(verb, path, filter); |
77 | | - } |
| 82 | + private String name = "route" + index; |
78 | 83 |
|
79 | | - } |
| 84 | + /** |
| 85 | + * A route pattern. |
| 86 | + */ |
| 87 | + private RoutePattern compiledPattern; |
80 | 88 |
|
81 | 89 | /** |
82 | | - * @return A route pattern. |
| 90 | + * The target route. |
83 | 91 | */ |
84 | | - RoutePattern path(); |
| 92 | + private Filter filter; |
| 93 | + |
| 94 | + /** |
| 95 | + * Defines the media types that the methods of a resource class or can accept. Default is: |
| 96 | + * {@literal *}/{@literal *}. |
| 97 | + */ |
| 98 | + private List<MediaType> consumes = ALL; |
| 99 | + |
| 100 | + /** |
| 101 | + * Defines the media types that the methods of a resource class or can produces. Default is: |
| 102 | + * {@literal *}/{@literal *}. |
| 103 | + */ |
| 104 | + private List<MediaType> produces = ALL; |
| 105 | + private String verb; |
| 106 | + |
| 107 | + private String pattern; |
| 108 | + |
| 109 | + /** |
| 110 | + * Creates a new {@link RouteDefinitionImpl}. |
| 111 | + * |
| 112 | + * @param verb A HTTP verb. |
| 113 | + * @param pattern |
| 114 | + * @param filter |
| 115 | + */ |
| 116 | + private RouteDefinition(final String verb, final String pattern, final Filter filter) { |
| 117 | + requireNonNull(verb, "A HTTP verb is required."); |
| 118 | + requireNonNull(pattern, "A route path is required."); |
| 119 | + requireNonNull(filter, "A filter is required."); |
| 120 | + |
| 121 | + this.verb = verb; |
| 122 | + this.pattern = pattern; |
| 123 | + this.compiledPattern = new RoutePattern(verb, pattern); |
| 124 | + this.filter = filter; |
| 125 | + } |
| 126 | + |
| 127 | + public String pattern() { |
| 128 | + return pattern; |
| 129 | + } |
| 130 | + |
| 131 | + public Optional<Route> matches(final String verb, final String path, final MediaType contentType, |
| 132 | + final List<MediaType> accept) { |
| 133 | + RouteMatcher matcher = compiledPattern.matcher(verb.toUpperCase() + path); |
| 134 | + if (matcher.matches()) { |
| 135 | + List<MediaType> result = MediaType.matcher(accept).filter(this.produces); |
| 136 | + if (canConsume(contentType) && result.size() > 0) { |
| 137 | + // keep accept when */* |
| 138 | + List<MediaType> produces = result.size() == 1 && result.get(0).name().equals("*/*") |
| 139 | + ? accept : this.produces; |
| 140 | + return Optional.of(asRoute(matcher, produces)); |
| 141 | + } |
| 142 | + } |
| 143 | + return Optional.empty(); |
| 144 | + } |
85 | 145 |
|
86 | | - int index(); |
| 146 | + private Route asRoute(final RouteMatcher matcher, final List<MediaType> produces) { |
| 147 | + return new RouteImpl(filter, verb, matcher.path(), pattern, name, index, matcher.vars(), consumes, |
| 148 | + produces); |
| 149 | + } |
87 | 150 |
|
88 | | - String name(); |
| 151 | + public String verb() { |
| 152 | + return verb; |
| 153 | + } |
89 | 154 |
|
90 | | - RouteDefinition name(String name); |
| 155 | + public int index() { |
| 156 | + return index; |
| 157 | + } |
| 158 | + |
| 159 | + public String name() { |
| 160 | + return name; |
| 161 | + } |
| 162 | + |
| 163 | + public RouteDefinition name(final String name) { |
| 164 | + this.name = requireNonNull(name, "A route's name is required."); |
| 165 | + return this; |
| 166 | + } |
91 | 167 |
|
92 | 168 | /** |
93 | 169 | * @param candidate A media type to test. |
94 | 170 | * @return True, if the route can consume the given media type. |
95 | 171 | */ |
96 | | - boolean canConsume(@Nonnull MediaType candidate); |
| 172 | + public boolean canConsume(@Nonnull final MediaType candidate) { |
| 173 | + return MediaType.matcher(Arrays.asList(candidate)).matches(consumes); |
| 174 | + } |
97 | 175 |
|
98 | 176 | /** |
99 | 177 | * @param candidates A media types to test. |
100 | 178 | * @return True, if the route can produces the given media type. |
101 | 179 | */ |
102 | | - boolean canProduce(List<MediaType> candidates); |
| 180 | + public boolean canProduce(final List<MediaType> candidates) { |
| 181 | + return MediaType.matcher(candidates).matches(produces); |
| 182 | + } |
103 | 183 |
|
104 | 184 | /** |
105 | 185 | * Set the media types the route can consume. |
106 | 186 | * |
107 | 187 | * @param consumes The media types to test for. |
108 | 188 | * @return This route definition. |
109 | 189 | */ |
110 | | - RouteDefinition consumes(MediaType... consumes); |
| 190 | + public RouteDefinition consumes(final MediaType... consumes) { |
| 191 | + return consumes(Arrays.asList(consumes)); |
| 192 | + } |
111 | 193 |
|
112 | 194 | /** |
113 | 195 | * Set the media types the route can consume. |
114 | 196 | * |
115 | 197 | * @param consumes The media types to test for. |
116 | 198 | * @return This route definition. |
117 | 199 | */ |
118 | | - RouteDefinition consumes(Iterable<MediaType> consumes); |
| 200 | + public RouteDefinition consumes(final Iterable<MediaType> consumes) { |
| 201 | + this.consumes = Lists.newArrayList(consumes); |
| 202 | + Collections.sort(this.consumes); |
| 203 | + return this; |
| 204 | + } |
119 | 205 |
|
120 | 206 | /** |
121 | 207 | * Set the media types the route can produces. |
122 | 208 | * |
123 | 209 | * @param produces The media types to test for. |
124 | 210 | * @return This route definition. |
125 | 211 | */ |
126 | | - RouteDefinition produces(MediaType... produces); |
| 212 | + public RouteDefinition produces(final MediaType... produces) { |
| 213 | + return produces(Arrays.asList(produces)); |
| 214 | + } |
127 | 215 |
|
128 | 216 | /** |
129 | 217 | * Set the media types the route can produces. |
130 | 218 | * |
131 | 219 | * @param produces The media types to test for. |
132 | 220 | * @return This route definition. |
133 | 221 | */ |
134 | | - RouteDefinition produces(Iterable<MediaType> produces); |
| 222 | + public RouteDefinition produces(final Iterable<MediaType> produces) { |
| 223 | + this.produces = Lists.newArrayList(produces); |
| 224 | + Collections.sort(this.produces); |
| 225 | + return this; |
| 226 | + } |
135 | 227 |
|
136 | 228 | /** |
137 | 229 | * @return All the types this route can consumes. |
138 | 230 | */ |
139 | | - List<MediaType> consumes(); |
| 231 | + public List<MediaType> consumes() { |
| 232 | + return ImmutableList.copyOf(this.consumes); |
| 233 | + } |
140 | 234 |
|
141 | 235 | /** |
142 | 236 | * @return All the types this route can produces. |
143 | 237 | */ |
144 | | - List<MediaType> produces(); |
| 238 | + public List<MediaType> produces() { |
| 239 | + return ImmutableList.copyOf(this.produces); |
| 240 | + } |
| 241 | + |
| 242 | + public static RouteDefinition newRoute(final String verb, final String path, |
| 243 | + final Router router) { |
| 244 | + return new RouteDefinition(verb, path, (req, res, chain) -> { |
| 245 | + router.handle(req, res); |
| 246 | + chain.next(req, res); |
| 247 | + }); |
| 248 | + } |
| 249 | + |
| 250 | + public static RouteDefinition newRoute(final String verb, final String path, |
| 251 | + final Filter filter) { |
| 252 | + return new RouteDefinition(verb, path, filter); |
| 253 | + } |
145 | 254 |
|
146 | 255 | } |
0 commit comments