Author: ate
Date: Mon Sep 17 22:46:48 2012
New Revision: 1386891
URL: http://svn.apache.org/viewvc?rev=1386891&view=rev
Log:
RAVE-694: rave-web-hmvc
- introduce Route mapped HandlerInterceptor lists
- drop setters from Route interface
- add an abstract PreHandleModelHandlerInterceptor which can be used to inject
and prepare a (Routed/Nested) Controller model (very useful when used together
with a route mapped HandlerInterceptor list ...)
- expose matched and 'executing' Route through the MethodHandler HandlerConfig
Added:
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/PreHandleModelHandlerInterceptor.java
(with props)
Modified:
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/HandlerConfig.java
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/HandlerConfigImpl.java
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/NestedHandlerMethodHandlerAdapter.java
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/NestedHandlerMethodHandlerMapping.java
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/RoutedHandlerMethod.java
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/RoutedRequestMappingHandlerMapping.java
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/config/Route.java
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/config/xml/XmlRoute.java
rave/sandbox/content-services/rave-web-jcr/src/main/java/org/apache/rave/portal/web/mvc/config/jcr/JcrRoute.java
Modified:
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/HandlerConfig.java
URL:
http://svn.apache.org/viewvc/rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/HandlerConfig.java?rev=1386891&r1=1386890&r2=1386891&view=diff
==============================================================================
---
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/HandlerConfig.java
(original)
+++
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/HandlerConfig.java
Mon Sep 17 22:46:48 2012
@@ -16,7 +16,10 @@
*/
package org.apache.rave.portal.web.mvc;
+import org.apache.rave.portal.web.mvc.config.Route;
+
public interface HandlerConfig {
+ Route getRoute();
String getName();
Class getType();
String getAction();
Modified:
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/HandlerConfigImpl.java
URL:
http://svn.apache.org/viewvc/rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/HandlerConfigImpl.java?rev=1386891&r1=1386890&r2=1386891&view=diff
==============================================================================
---
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/HandlerConfigImpl.java
(original)
+++
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/HandlerConfigImpl.java
Mon Sep 17 22:46:48 2012
@@ -16,11 +16,13 @@
*/
package org.apache.rave.portal.web.mvc;
+import org.apache.rave.portal.web.mvc.config.Route;
import org.springframework.web.method.HandlerMethod;
public class HandlerConfigImpl implements HandlerConfig {
private String name;
private String action;
+ protected Route route;
protected RoutedHandlerMethod method;
@Override
@@ -46,6 +48,15 @@ public class HandlerConfigImpl implement
this.name = name;
}
+ @Override
+ public Route getRoute() {
+ return route;
+ }
+
+ public void setRoute(final Route route) {
+ this.route = route;
+ }
+
public RoutedHandlerMethod getMethod() {
return method;
}
Modified:
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/NestedHandlerMethodHandlerAdapter.java
URL:
http://svn.apache.org/viewvc/rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/NestedHandlerMethodHandlerAdapter.java?rev=1386891&r1=1386890&r2=1386891&view=diff
==============================================================================
---
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/NestedHandlerMethodHandlerAdapter.java
(original)
+++
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/NestedHandlerMethodHandlerAdapter.java
Mon Sep 17 22:46:48 2012
@@ -24,9 +24,11 @@ import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.springframework.ui.ExtendedModelMap;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.DispatcherServlet;
+import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.ModelAndView;
import
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
@@ -45,6 +47,17 @@ public class NestedHandlerMethodHandlerA
protected ModelAndView handleNested(HttpServletRequest request,
HttpServletResponse response,
HandlerMethod handlerMethod) throws
Exception {
+ ExtendedModelMap preHandleModelMap =
(ExtendedModelMap)request.getAttribute(RoutedHandlerMethod.PREHANDLE_INTERCEPTOR_MODEL);
+ // only do this once
+
request.removeAttribute(RoutedHandlerMethod.PREHANDLE_INTERCEPTOR_MODEL);
+ if (preHandleModelMap != null && !preHandleModelMap.isEmpty()) {
+ FlashMap flashMap =
(FlashMap)request.getAttribute(DispatcherServlet.INPUT_FLASH_MAP_ATTRIBUTE);
+ if (flashMap != null) {
+ // merge models
+ preHandleModelMap.addAllAttributes(flashMap);
+ }
+ request.setAttribute(DispatcherServlet.INPUT_FLASH_MAP_ATTRIBUTE,
preHandleModelMap);
+ }
NestedHandlerMethod hm = (handlerMethod instanceof
NestedHandlerMethod) ? (NestedHandlerMethod)handlerMethod : null;
if (hm != null) {
NestedHandlerContextImpl hc =
(NestedHandlerContextImpl)request.getAttribute(NestedHandlerMethod.HANDLER_CONTEXT);
Modified:
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/NestedHandlerMethodHandlerMapping.java
URL:
http://svn.apache.org/viewvc/rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/NestedHandlerMethodHandlerMapping.java?rev=1386891&r1=1386890&r2=1386891&view=diff
==============================================================================
---
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/NestedHandlerMethodHandlerMapping.java
(original)
+++
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/NestedHandlerMethodHandlerMapping.java
Mon Sep 17 22:46:48 2012
@@ -17,6 +17,7 @@
package org.apache.rave.portal.web.mvc;
import java.lang.reflect.Method;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -57,7 +58,7 @@ public class NestedHandlerMethodHandlerM
if (ami != null) {
try {
Object handler =
getApplicationContext().getBean(controllerClass);
- registerPageHandlerMethod(route.getPath(),
page, handler, ami.method, ami.info, action);
+ registerPageHandlerMethod(route, page,
handler, ami.method, ami.info, action);
}
catch (Exception e) {
log.error("Error", e);
@@ -69,8 +70,8 @@ public class NestedHandlerMethodHandlerM
}
}
- protected void registerPageHandlerMethod(String routePath, PageFragment
pageDefinition, Object handler, Method method, RequestMappingInfo mapping,
String action) {
- NestedHandlerMethod nestedHandlerMethod =
getNestedHandlerMethodForFragment(routePath, handler, method, pageDefinition,
action);
+ protected void registerPageHandlerMethod(Route route, PageFragment
pageDefinition, Object handler, Method method, RequestMappingInfo mapping,
String action) {
+ NestedHandlerMethod nestedHandlerMethod =
getNestedHandlerMethodForFragment(route, handler, method, pageDefinition,
action);
registerHandlerMethod(handler, method, mapping);
registerTargetActionPath(pageDefinition.getName(), action, mapping);
// replace default HandlerMethod with our own nestedHandlerMethod
@@ -80,10 +81,11 @@ public class NestedHandlerMethodHandlerM
}
}
- private NestedHandlerMethod getNestedHandlerMethodForFragment(String
routePath, Object handler, Method method, PageFragment fragment, String action)
{
+ private NestedHandlerMethod getNestedHandlerMethodForFragment(Route route,
Object handler, Method method, PageFragment fragment, String action) {
NestedHandlerConfigImpl config = new NestedHandlerConfigImpl();
config.setName(fragment.getName());
config.setViewName(fragment.getViewName());
+ config.setRoute(route);
config.setMethod(new NestedHandlerMethod(handler, method, config));
final List<PageFragment> children = fragment.getChildren();
@@ -91,7 +93,7 @@ public class NestedHandlerMethodHandlerM
for (PageFragment child : children) {
Class<?> controllerClass =
getControllerClass(child.getController());
if (controllerClass != null) {
- Map<String, ActionMethodInfo> infos =
getActionMethodInfos(controllerClass, routePath, true);
+ Map<String, ActionMethodInfo> infos =
getActionMethodInfos(controllerClass, route.getPath(), true);
ActionMethodInfo ami = infos.get(action);
if (ami == null && !"default".equals(action)) {
ami = infos.get("default");
@@ -104,7 +106,7 @@ public class NestedHandlerMethodHandlerM
throw new IllegalStateException("Cannot
instantiate found Child Controller");
}
log.info("@@@ Registering child component [{}] for
parent [{}] ", child.getName(), fragment.getName());
- NestedHandlerConfigImpl childConfig =
getNestedHandlerMethodForFragment(routePath, ccInstance, ami.method, child,
action).getConfiguration();
+ NestedHandlerConfigImpl childConfig =
getNestedHandlerMethodForFragment(route, ccInstance, ami.method, child,
action).getConfiguration();
childConfig.setParent(config);
config.getChildren().put(child.getName(), childConfig);
}
Added:
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/PreHandleModelHandlerInterceptor.java
URL:
http://svn.apache.org/viewvc/rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/PreHandleModelHandlerInterceptor.java?rev=1386891&view=auto
==============================================================================
---
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/PreHandleModelHandlerInterceptor.java
(added)
+++
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/PreHandleModelHandlerInterceptor.java
Mon Sep 17 22:46:48 2012
@@ -0,0 +1,44 @@
+package org.apache.rave.portal.web.mvc;
+
+import java.util.Collections;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.ui.ExtendedModelMap;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.HandlerMapping;
+import org.springframework.web.servlet.ModelAndView;
+
+public abstract class PreHandleModelHandlerInterceptor implements
HandlerInterceptor {
+
+ @Override
+ public final boolean preHandle(final HttpServletRequest request, final
HttpServletResponse response, final Object handler) throws Exception {
+ ExtendedModelMap m =
(ExtendedModelMap)request.getAttribute(RoutedHandlerMethod.PREHANDLE_INTERCEPTOR_MODEL);
+ if (m == null) {
+ m = new ExtendedModelMap();
+
request.setAttribute(RoutedHandlerMethod.PREHANDLE_INTERCEPTOR_MODEL, m);
+ }
+ return preHandle(request, response, handler, m);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected final Map<String, String>
getUriTemplateVariables(HttpServletRequest request) {
+ Map<String, String> variables =
+ (Map<String, String>)
request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
+ return (variables != null) ? variables : Collections.<String,
String>emptyMap();
+ }
+
+ protected abstract boolean preHandle(final HttpServletRequest request,
final HttpServletResponse response, final Object handler, final
ExtendedModelMap modelMap) throws Exception;
+
+ @Override
+ public void postHandle(final HttpServletRequest request, final
HttpServletResponse response, final Object handler, final ModelAndView
modelAndView) throws Exception {
+ // default do nothing: override to customize behavior
+ }
+
+ @Override
+ public void afterCompletion(final HttpServletRequest request, final
HttpServletResponse response, final Object handler, final Exception ex) throws
Exception {
+ // default do nothing: override to customize behavior
+ }
+}
Propchange:
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/PreHandleModelHandlerInterceptor.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/PreHandleModelHandlerInterceptor.java
------------------------------------------------------------------------------
svn:keywords = Id
Modified:
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/RoutedHandlerMethod.java
URL:
http://svn.apache.org/viewvc/rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/RoutedHandlerMethod.java?rev=1386891&r1=1386890&r2=1386891&view=diff
==============================================================================
---
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/RoutedHandlerMethod.java
(original)
+++
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/RoutedHandlerMethod.java
Mon Sep 17 22:46:48 2012
@@ -28,6 +28,7 @@ public class RoutedHandlerMethod extends
public static final String MODEL_AND_VIEW =
RoutedHandlerMethod.class.getName() + ".ModelAndView";
public static final String HANDLER_CONTEXT =
RoutedHandlerMethod.class.getName() + ".HandlerContext";
public static final String TARGET_ACTION_PATH_MAP =
RoutedHandlerMethod.class.getName() + ".TargetActionPathMap";
+ public static final String PREHANDLE_INTERCEPTOR_MODEL =
RoutedHandlerMethod.class.getName() + ".PreHandleModel";
private HandlerConfigImpl configuration;
private BeanFactory beanFactory;
Modified:
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/RoutedRequestMappingHandlerMapping.java
URL:
http://svn.apache.org/viewvc/rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/RoutedRequestMappingHandlerMapping.java?rev=1386891&r1=1386890&r2=1386891&view=diff
==============================================================================
---
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/RoutedRequestMappingHandlerMapping.java
(original)
+++
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/RoutedRequestMappingHandlerMapping.java
Mon Sep 17 22:46:48 2012
@@ -43,6 +43,8 @@ import org.springframework.web.bind.anno
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.HandlerMethodSelector;
+import org.springframework.web.servlet.HandlerExecutionChain;
+import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition;
@@ -80,7 +82,7 @@ public class RoutedRequestMappingHandler
private Map<RequestMappingInfo, HandlerMethod> handlerMethods;
private MultiValueMap<String, RequestMappingInfo> urlMap = new
LinkedMultiValueMap<String, RequestMappingInfo>();
-// private Credentials credentials;
+ Map<String, List<HandlerInterceptor>> routeInterceptors = new
HashMap<String, List<HandlerInterceptor>>();
public RoutedRequestMappingHandlerMapping() {
super();
@@ -123,6 +125,12 @@ public class RoutedRequestMappingHandler
return targetActionPathMap;
}
+ public void setRouteInterceptors(Map<String, List<HandlerInterceptor>>
map) {
+ if (map != null && !map.isEmpty()) {
+ this.routeInterceptors.putAll(map);
+ }
+ }
+
protected void registerTargetActionPath(String target, String action,
RequestMappingInfo info) {
registerTargetActionPath(target, action,
info.getPatternsCondition().getPatterns().iterator().next());
}
@@ -262,6 +270,7 @@ public class RoutedRequestMappingHandler
HandlerConfigImpl config = new HandlerConfigImpl();
config.setName(controller);
config.setAction(route.getAction());
+ config.setRoute(route);
config.setMethod(new RoutedHandlerMethod(handler,
ami.method, config));
registerHandlerMethod(handler, ami.method,
ami.info);
registerTargetActionPath(controller, ami.action,
ami.info);
@@ -395,4 +404,21 @@ public class RoutedRequestMappingHandler
protected Config getConfig() {
return config;
}
+
+ protected HandlerExecutionChain getHandlerExecutionChain(Object handler,
HttpServletRequest request) {
+ HandlerExecutionChain chain = super.getHandlerExecutionChain(handler,
request);
+ HandlerContext handlerContext =
(HandlerContext)request.getAttribute(RoutedHandlerMethod.HANDLER_CONTEXT);
+ if (handlerContext != null) {
+ Route route = handlerContext.getConfig().getRoute();
+ if (route.getInterceptors() != null) {
+ List<HandlerInterceptor> interceptors =
routeInterceptors.get(route.getInterceptors());
+ if (interceptors != null) {
+ for (HandlerInterceptor interceptor : interceptors) {
+ chain.addInterceptor(interceptor);
+ }
+ }
+ }
+ }
+ return chain;
+ }
}
\ No newline at end of file
Modified:
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/config/Route.java
URL:
http://svn.apache.org/viewvc/rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/config/Route.java?rev=1386891&r1=1386890&r2=1386891&view=diff
==============================================================================
---
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/config/Route.java
(original)
+++
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/config/Route.java
Mon Sep 17 22:46:48 2012
@@ -28,17 +28,13 @@ public interface Route {
List<Route> getChildren();
- void setChildren(List<Route> children);
-
String getPath();
void setPath(String path);
String getTarget();
- void setTarget(String target);
-
String getAction();
- void setAction(String action);
+ String getInterceptors();
}
Modified:
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/config/xml/XmlRoute.java
URL:
http://svn.apache.org/viewvc/rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/config/xml/XmlRoute.java?rev=1386891&r1=1386890&r2=1386891&view=diff
==============================================================================
---
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/config/xml/XmlRoute.java
(original)
+++
rave/sandbox/content-services/rave-web-hmvc/src/main/java/org/apache/rave/portal/web/mvc/config/xml/XmlRoute.java
Mon Sep 17 22:46:48 2012
@@ -34,6 +34,7 @@ public class XmlRoute implements Route {
private String path;
private String target;
private String action;
+ private String interceptors;
private List<Route> children;
public XmlRoute() {
@@ -58,7 +59,6 @@ public class XmlRoute implements Route {
return target;
}
- @Override
public void setTarget(String target) {
this.target = target;
}
@@ -69,11 +69,19 @@ public class XmlRoute implements Route {
return action;
}
- @Override
public void setAction(String action) {
this.action = action;
}
+ @XmlAttribute
+ @Override
+ public String getInterceptors() {
+ return interceptors;
+ }
+
+ public void setInterceptors(final String interceptors) {
+ this.interceptors = interceptors;
+ }
@XmlElement(name = "route", type = XmlRoute.class)
@Override
@@ -81,7 +89,6 @@ public class XmlRoute implements Route {
return children;
}
- @Override
public void setChildren(List<Route> children) {
this.children = children;
}
@@ -94,6 +101,7 @@ public class XmlRoute implements Route {
sb.append("{path='").append(path).append('\'');
sb.append("{target='").append(target).append('\'');
sb.append("{action ='").append(action).append('\'');
+ sb.append("{interceptors ='").append(interceptors).append('\'');
sb.append('}');
return sb.toString();
}
Modified:
rave/sandbox/content-services/rave-web-jcr/src/main/java/org/apache/rave/portal/web/mvc/config/jcr/JcrRoute.java
URL:
http://svn.apache.org/viewvc/rave/sandbox/content-services/rave-web-jcr/src/main/java/org/apache/rave/portal/web/mvc/config/jcr/JcrRoute.java?rev=1386891&r1=1386890&r2=1386891&view=diff
==============================================================================
---
rave/sandbox/content-services/rave-web-jcr/src/main/java/org/apache/rave/portal/web/mvc/config/jcr/JcrRoute.java
(original)
+++
rave/sandbox/content-services/rave-web-jcr/src/main/java/org/apache/rave/portal/web/mvc/config/jcr/JcrRoute.java
Mon Sep 17 22:46:48 2012
@@ -46,6 +46,9 @@ public class JcrRoute implements Route {
@Field
private String action;
+ @Field
+ private String interceptors;
+
@Collection(elementClassName = JcrRoute.class, jcrType =
"raveconfig:route", collectionConverter = NTCollectionConverterImpl.class)
private List<Route> children;
@@ -64,7 +67,6 @@ public class JcrRoute implements Route {
return target;
}
- @Override
public void setTarget(String target) {
this.target = target;
}
@@ -74,17 +76,24 @@ public class JcrRoute implements Route {
return action;
}
- @Override
public void setAction(final String action) {
this.action = action;
}
@Override
+ public String getInterceptors() {
+ return interceptors;
+ }
+
+ public void setInterceptors(final String interceptors) {
+ this.interceptors = interceptors;
+ }
+
+ @Override
public List<Route> getChildren() {
return children;
}
- @Override
public void setChildren(List<Route> children) {
this.children = children;
}
@@ -96,6 +105,7 @@ public class JcrRoute implements Route {
sb.append("{path='").append(path).append('\'');
sb.append(", target='").append(target).append('\'');
sb.append(", action='").append(action).append('\'');
+ sb.append(", interceptors='").append(interceptors).append('\'');
sb.append(", children=").append(children);
sb.append('}');
return sb.toString();