private ViewConfigNode findNodeWithClass(Class nodeClass, ViewConfigNode viewConfigNode) {
    if (viewConfigNode == null || nodeClass == null) {
      return null;
    }

    if (nodeClass.equals(viewConfigNode.getSource())) {
      return viewConfigNode;
    }
    return findNodeWithClass(nodeClass, viewConfigNode.getParent());
  }
  // the meta-data returned by this method is merged and potentially customized by a
  // ConfigPreProcessor
  private Annotation getFinalMetaDataFromNode(
      ViewConfigNode viewConfigNode, Annotation annotation) {
    Class<? extends Annotation> targetType = annotation.annotationType();

    // skip @View and @Folder, because they get created dynamically to support their optional usage
    // the dynamic generation depends on the level and if it is a synthetic information
    if (View.class.equals(targetType) || Folder.class.equals(targetType)) {
      return annotation;
    }

    // skip aggregated meta-data, because it can't be replaced
    // (there is no info available about the instance which replaced the original one
    // which might be equivalent to the annotation passed to this method)
    ViewMetaData viewMetaData = annotation.annotationType().getAnnotation(ViewMetaData.class);
    if (viewMetaData == null) {
      return annotation;
    }
    Aggregated aggregated = viewMetaData.annotationType().getAnnotation(Aggregated.class);
    if (aggregated == null || aggregated.value()) {
      return annotation;
    }

    for (Annotation nodeMetaData : viewConfigNode.getMetaData()) {
      if (targetType.equals(nodeMetaData.annotationType())) {
        return nodeMetaData;
      }
    }
    return annotation;
  }
  @Override
  public List<Annotation> resolveInheritedMetaData(ViewConfigNode viewConfigNode) {
    List<Annotation> inheritedAnnotations = new ArrayList<Annotation>();

    Set<Class> processedTypes = new HashSet<Class>();
    processedTypes.add(ViewConfig.class); // filter the base interface in any case

    Stack<Class> classesToAnalyze = new Stack<Class>();
    addInterfaces(
        processedTypes,
        classesToAnalyze,
        viewConfigNode.getSource()); // don't add the page-class itself

    while (!classesToAnalyze.empty()) {
      Class currentClass = classesToAnalyze.pop();

      if (processedTypes.contains(currentClass)) {
        continue;
      }
      processedTypes.add(currentClass);

      addInterfaces(processedTypes, classesToAnalyze, currentClass);

      // don't add the annotations of the final view-config class itself (we just need the inherited
      // annotations)
      if (ViewConfigUtils.isFolderConfig(currentClass)) {
        inheritedAnnotations.addAll(findViewMetaData(currentClass, viewConfigNode));
      }

      Class nextClass = currentClass.getSuperclass();
      if (nextClass != null && !Object.class.equals(nextClass)) {
        if (!processedTypes.contains(nextClass)) {
          classesToAnalyze.push(nextClass);
        }
      }
    }

    // add meta-data inherited via stereotypes on the node itself
    inheritedAnnotations.addAll(findViewMetaData(viewConfigNode.getSource(), viewConfigNode));

    return inheritedAnnotations;
  }
    // for a real implementation see e.g.:
    // org.apache.deltaspike.security.api.authorization.annotation.Secured
    // org.apache.deltaspike.jsf.api.config.view.controller.PageBean
    @Override
    public TestSecured beforeAddToConfig(TestSecured metaData, ViewConfigNode viewConfigNode) {
      List<CallbackDescriptor> descriptors =
          viewConfigNode.getCallbackDescriptors(TestSecured.class);

      if (descriptors.isEmpty()) // just for testing different constellations - usually not needed!
      {
        descriptors.add(new TestSecuredDescriptor(metaData.value(), DefaultCallback.class));
      }
      return metaData; // no change needed
    }
  @Test
  public void testSimpleMetaDataTreeWithMetaData() {
    this.viewConfigExtension.addPageDefinition(SimplePageConfig002.class);

    ViewConfigNode node = this.viewConfigExtension.findNode(SimplePageConfig002.class);

    Assert.assertNotNull(node);
    Assert.assertNotNull(node.getParent());
    Assert.assertNull(node.getParent().getParent());

    Assert.assertNotNull(node.getChildren());
    Assert.assertEquals(0, node.getChildren().size());

    Assert.assertNotNull(node.getMetaData());
    Assert.assertEquals(1, node.getMetaData().size());

    Assert.assertNotNull(node.getInheritedMetaData());
    Assert.assertEquals(0, node.getInheritedMetaData().size());
  }
  protected List<Annotation> findViewMetaData(Class currentClass, ViewConfigNode viewConfigNode) {
    // don't include meta-data from the node itself, because it would be stored as inherited
    // meta-data
    if (currentClass.equals(viewConfigNode.getSource())) {
      return Collections.emptyList();
    }

    List<Annotation> result = new ArrayList<Annotation>();

    for (Annotation annotation : currentClass.getAnnotations()) {
      Class<? extends Annotation> annotationClass = annotation.annotationType();

      if (annotationClass.getName().startsWith("java")) {
        continue;
      }

      addViewMetaData(annotation, result);
    }

    result =
        tryToReplaceWithMergedMetaDataFromAncestor(
            currentClass, viewConfigNode.getParent(), result);
    return result;
  }