PY-4717 Remove old NumPyDocString parser
authorMikhail Golubev <mikhail.golubev@jetbrains.com>
Tue, 15 Sep 2015 14:31:08 +0000 (17:31 +0300)
committerMikhail Golubev <mikhail.golubev@jetbrains.com>
Thu, 17 Sep 2015 10:01:29 +0000 (13:01 +0300)
Move its parts for pre-parsing of Numpy-specific type formats to
NumpyDocStringTypeProvider, e.g. stripping ", optional" from types,
splitting implicit union types separated by comma etc.

python/src/com/jetbrains/numpy/codeInsight/NumpyDocStringTypeProvider.java
python/src/com/jetbrains/numpy/documentation/NumPyDocString.java [deleted file]
python/src/com/jetbrains/python/documentation/docstrings/SectionBasedDocString.java

index b46fe8b7a4ffae7ce2efb7029caaad7c6bdd5026..1406bbc3cce94a9886841e8ac8b0770fb11a02b5 100644 (file)
@@ -22,11 +22,16 @@ import com.intellij.openapi.module.ModuleUtilCore;
 import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiDirectory;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
-import com.jetbrains.numpy.documentation.NumPyDocString;
-import com.jetbrains.numpy.documentation.NumPyDocStringParameter;
+import com.intellij.psi.util.QualifiedName;
+import com.jetbrains.python.PyNames;
 import com.jetbrains.python.documentation.PyDocumentationSettings;
+import com.jetbrains.python.documentation.docstrings.DocStringFormat;
+import com.jetbrains.python.documentation.docstrings.DocStringUtil;
+import com.jetbrains.python.documentation.docstrings.NumpyDocString;
+import com.jetbrains.python.documentation.docstrings.SectionBasedDocString.SectionField;
 import com.jetbrains.python.psi.*;
 import com.jetbrains.python.psi.impl.PyBuiltinCache;
 import com.jetbrains.python.psi.impl.PyExpressionCodeFragmentImpl;
@@ -34,10 +39,13 @@ import com.jetbrains.python.psi.types.PyNoneType;
 import com.jetbrains.python.psi.types.PyType;
 import com.jetbrains.python.psi.types.PyTypeProviderBase;
 import com.jetbrains.python.psi.types.TypeEvalContext;
+import com.jetbrains.python.toolbox.Substring;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Provides type information extracted from NumPy docstring format.
@@ -47,6 +55,9 @@ import java.util.*;
  */
 public class NumpyDocStringTypeProvider extends PyTypeProviderBase {
   private static final Map<String, String> NUMPY_ALIAS_TO_REAL_TYPE = new HashMap<String, String>();
+  private static final Pattern REDIRECT = Pattern.compile("^Refer to `(.*)` for full documentation.$");
+  private static final Pattern NUMPY_UNION_PATTERN = Pattern.compile("^\\{(.*)\\}$");
+  private static final Pattern NUMPY_ARRAY_PATTERN = Pattern.compile("(\\(\\.\\.\\..*\\))(.*)");
   public static String NDARRAY = "numpy.core.multiarray.ndarray";
 
   private static String NDARRAY_OR_ITERABLE = NDARRAY + " or collections.Iterable";
@@ -89,14 +100,116 @@ public class NumpyDocStringTypeProvider extends PyTypeProviderBase {
     NUMPY_ALIAS_TO_REAL_TYPE.put("ints", "int");
   }
 
+  @Nullable
+  private static NumpyDocString forFunction(@NotNull PyFunction function, @Nullable PsiElement reference, @Nullable String knownSignature) {
+    String docString = function.getDocStringValue();
+    if (docString == null && PyNames.INIT.equals(function.getName())) {
+      // Docstring for constructor can be found in the docstring of class
+      PyClass cls = function.getContainingClass();
+      if (cls != null) {
+        docString = cls.getDocStringValue();
+      }
+    }
+
+    if (docString != null) {
+      final NumpyDocString parsed = (NumpyDocString)DocStringUtil.parseDocString(DocStringFormat.NUMPY, docString);
+      if (parsed.getReturnFields().isEmpty() && parsed.getParameterFields().isEmpty()) {
+        return null;
+      }
+
+      String signature = parsed.getSignature();
+      String redirect = findRedirect(parsed.getLines());
+      if (redirect != null && reference != null) {
+        PyFunction resolvedFunction = resolveRedirectToFunction(redirect, reference);
+        if (resolvedFunction != null) {
+          return forFunction(resolvedFunction, reference, knownSignature != null ? knownSignature : signature);
+        }
+      }
+      return parsed;
+    }
+    return null;
+  }
+
+  /**
+   * Returns NumPyDocString object confirming to Numpy-style formatted docstring of specified function.
+   *
+   * @param function  Function containing docstring for which Numpy wrapper object is to be obtained.
+   * @param reference An original reference element to specified function.
+   * @return Numpy docstring wrapper object for specified function.
+   */
+  @Nullable
+  public static NumpyDocString forFunction(@NotNull PyFunction function, @Nullable PsiElement reference) {
+    return forFunction(function, reference, null);
+  }
+
+  @Nullable
+  private static String findRedirect(@NotNull List<Substring> lines) {
+    for (Substring line : lines) {
+      Matcher matcher = REDIRECT.matcher(line);
+      if (matcher.matches() && matcher.groupCount() > 0) {
+        return matcher.group(1);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Returns PyFunction object for specified fully qualified name accessible from specified reference.
+   *
+   * @param redirect  A fully qualified name of function that is redirected to.
+   * @param reference An original reference element.
+   * @return Resolved function or null if it was not resolved.
+   */
+  @Nullable
+  private static PyFunction resolveRedirectToFunction(@NotNull String redirect, @NotNull PsiElement reference) {
+    final QualifiedName qualifiedName = QualifiedName.fromDottedString(redirect);
+    final String functionName = qualifiedName.getLastComponent();
+    final PyPsiFacade facade = PyPsiFacade.getInstance(reference.getProject());
+    final List<PsiElement> items = facade.qualifiedNameResolver(qualifiedName.removeLastComponent()).fromElement(reference).resultsAsList();
+    for (PsiElement item : items) {
+      if (item instanceof PsiDirectory) {
+        item = ((PsiDirectory)item).findFile(PyNames.INIT_DOT_PY);
+      }
+      if (item instanceof PyFile) {
+        final PsiElement element = ((PyFile)item).getElementNamed(functionName);
+        if (element instanceof PyFunction) {
+          return (PyFunction)element;
+        }
+      }
+    }
+    return null;
+  }
+
+  @Nullable
+  public static String cleanupOptional(@NotNull String typeString) {
+    int index = typeString.indexOf(", optional");
+    if (index >= 0) {
+      return typeString.substring(0, index);
+    }
+    return null;
+  }
+
+  @NotNull
+  public static List<String> getNumpyUnionType(@NotNull String typeString) {
+    final Matcher arrayMatcher = NUMPY_ARRAY_PATTERN.matcher(typeString);
+    if (arrayMatcher.matches()) {
+      typeString = arrayMatcher.group(2);
+    }
+    Matcher matcher = NUMPY_UNION_PATTERN.matcher(typeString);
+    if (matcher.matches()) {
+      typeString = matcher.group(1);
+    }
+    return Arrays.asList(typeString.split(" *, *"));
+  }
+
   @Nullable
   @Override
   public PyType getCallType(@NotNull PyFunction function, @Nullable PyCallSiteExpression callSite, @NotNull TypeEvalContext context) {
     if (isApplicable(function)) {
       final PyExpression callee = callSite instanceof PyCallExpression ? ((PyCallExpression)callSite).getCallee() : null;
-      final NumPyDocString docString = NumPyDocString.forFunction(function, callee);
+      final NumpyDocString docString = forFunction(function, callee);
       if (docString != null) {
-        final List<NumPyDocStringParameter> returns = docString.getReturns();
+        final List<SectionField> returns = docString.getReturnFields();
         final PyPsiFacade facade = getPsiFacade(function);
         switch (returns.size()) {
           case 0:
@@ -104,7 +217,7 @@ public class NumpyDocStringTypeProvider extends PyTypeProviderBase {
           case 1:
             // Function returns single value
             final String typeName = returns.get(0).getType();
-            if (typeName != null) {
+            if (!typeName.isEmpty()) {
               final PyType genericType = getPsiFacade(function).parseTypeAnnotation("T", function);
               if (isUfuncType(function, typeName)) return genericType;
               return parseNumpyDocType(function, typeName);
@@ -117,10 +230,10 @@ public class NumpyDocStringTypeProvider extends PyTypeProviderBase {
             final List<PyType> members = new ArrayList<PyType>();
 
             for (int i = 0; i < returns.size(); i++) {
-              NumPyDocStringParameter ret = returns.get(i);
+              SectionField ret = returns.get(i);
               final String memberTypeName = ret.getType();
-              final PyType returnType = memberTypeName != null ? parseNumpyDocType(function, memberTypeName) : null;
-              final boolean isOptional = memberTypeName != null && memberTypeName.contains("optional");
+              final PyType returnType = !memberTypeName.isEmpty() ? parseNumpyDocType(function, memberTypeName) : null;
+              final boolean isOptional = !memberTypeName.isEmpty() && memberTypeName.contains("optional");
 
               if (isOptional) {
                 if (i != 0) {
@@ -245,12 +358,12 @@ public class NumpyDocStringTypeProvider extends PyTypeProviderBase {
 
   @Nullable
   private static PyType parseNumpyDocType(@NotNull PsiElement anchor, @NotNull String typeString) {
-    final String withoutOptional = NumPyDocString.cleanupOptional(typeString);
+    final String withoutOptional = cleanupOptional(typeString);
     final Set<PyType> types = new LinkedHashSet<PyType>();
     if (withoutOptional != null) {
       typeString = withoutOptional;
     }
-    for (String typeName : NumPyDocString.getNumpyUnionType(typeString)) {
+    for (String typeName : getNumpyUnionType(typeString)) {
       PyType parsedType = parseSingleNumpyDocType(anchor, typeName);
       if (parsedType != null) {
         types.add(parsedType);
@@ -263,7 +376,7 @@ public class NumpyDocStringTypeProvider extends PyTypeProviderBase {
   }
 
   private static boolean isUfuncType(@NotNull PsiElement anchor, @NotNull final String typeString) {
-    for (String typeName : NumPyDocString.getNumpyUnionType(typeString)) {
+    for (String typeName : getNumpyUnionType(typeString)) {
       if (anchor instanceof PyFunction && isInsideNumPy(anchor) && NumpyUfuncs.isUFunc(((PyFunction)anchor).getName()) &&
           ("array_like".equals(typeName) || "ndarray".equals(typeName))) {
         return true;
@@ -274,14 +387,14 @@ public class NumpyDocStringTypeProvider extends PyTypeProviderBase {
 
   @Nullable
   private static PyType getParameterType(@NotNull PyFunction function, @NotNull String parameterName) {
-    final NumPyDocString docString = NumPyDocString.forFunction(function, function);
+    final NumpyDocString docString = forFunction(function, function);
     if (docString != null) {
-      NumPyDocStringParameter parameter = docString.getNamedParameter(parameterName);
+      SectionField parameter = docString.getFirstFieldForParameter(parameterName);
 
       // If parameter name starts with "p_", and we failed to obtain it from the docstring,
       // try to obtain parameter named without such prefix.
       if (parameter == null && parameterName.startsWith("p_")) {
-        parameter = docString.getNamedParameter(parameterName.substring(2));
+        parameter = docString.getFirstFieldForParameter(parameterName.substring(2));
       }
       if (parameter != null) {
         if (isUfuncType(function, parameter.getType())) {
diff --git a/python/src/com/jetbrains/numpy/documentation/NumPyDocString.java b/python/src/com/jetbrains/numpy/documentation/NumPyDocString.java
deleted file mode 100644 (file)
index 760369d..0000000
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * Copyright 2000-2014 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.numpy.documentation;
-
-import com.intellij.psi.PsiDirectory;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.util.QualifiedName;
-import com.jetbrains.python.PyNames;
-import com.jetbrains.python.psi.PyClass;
-import com.jetbrains.python.psi.PyFile;
-import com.jetbrains.python.psi.PyFunction;
-import com.jetbrains.python.psi.PyPsiFacade;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * @author avereshchagin
- * @author vlan
- */
-public class NumPyDocString {
-  private static final Pattern LINE_SEPARATOR = Pattern.compile("\n|\r|\r\n");
-  private static final Pattern WHITE_SPACED_LINE = Pattern.compile("^[ \t]+$");
-  private static final Pattern ANY_INDENT = Pattern.compile("(^[ \t]*)[^ \t\r\n]");
-  private static final Pattern HAS_INDENT = Pattern.compile("(^[ \t]+)[^ \t\r\n]");
-  private static final Pattern SIGNATURE = Pattern.compile("^([\\w., ]+=)?\\s*[\\w\\.]+\\(.*\\)$");
-  private static final Pattern SECTION_HEADER = Pattern.compile("^[-=]+");
-  private static final Pattern PARAMETER_WITH_TYPE = Pattern.compile("^(.+) : (.+)$");
-  private static final Pattern PARAMETER_WITHOUT_TYPE = Pattern.compile("^([^ :,]+)$");
-  private static final Pattern REDIRECT = Pattern.compile("^Refer to `(.*)` for full documentation.$");
-  private static final Pattern NUMPY_UNION_PATTERN = Pattern.compile("^\\{(.*)\\}$");
-  private static final Pattern NUMPY_ARRAY_PATTERN = Pattern.compile("(\\(\\.\\.\\..*\\))(.*)");
-
-  private final String mySignature;
-  private final List<NumPyDocStringParameter> myParameters = new ArrayList<NumPyDocStringParameter>();
-  private final List<NumPyDocStringParameter> myReturns = new ArrayList<NumPyDocStringParameter>();
-
-  private NumPyDocString(@Nullable String signature, @NotNull List<String> lines)
-    throws NotNumpyDocStringException {
-    mySignature = signature;
-    parseSections(lines);
-    if (myReturns.size() == 0 && myParameters.size() == 0) {
-      throw new NotNumpyDocStringException(signature);
-    }
-  }
-
-  @Nullable
-  public String getSignature() {
-    return mySignature;
-  }
-
-  @NotNull
-  public List<NumPyDocStringParameter> getParameters() {
-    return myParameters;
-  }
-
-  @NotNull
-  public List<NumPyDocStringParameter> getReturns() {
-    return myReturns;
-  }
-
-  @Nullable
-  public NumPyDocStringParameter getNamedParameter(@NotNull String name) {
-    for (NumPyDocStringParameter parameter : getParameters()) {
-      if (name.equals(parameter.getName())) {
-        return parameter;
-      }
-    }
-    return null;
-  }
-
-  /**
-   * Returns PyFunction object for specified fully qualified name accessible from specified reference.
-   *
-   * @param redirect  A fully qualified name of function that is redirected to.
-   * @param reference An original reference element.
-   * @return Resolved function or null if it was not resolved.
-   */
-  @Nullable
-  private static PyFunction resolveRedirectToFunction(@NotNull String redirect, @NotNull PsiElement reference) {
-    final QualifiedName qualifiedName = QualifiedName.fromDottedString(redirect);
-    final String functionName = qualifiedName.getLastComponent();
-    final PyPsiFacade facade = PyPsiFacade.getInstance(reference.getProject());
-    final List<PsiElement> items = facade.qualifiedNameResolver(qualifiedName.removeLastComponent()).fromElement(reference).resultsAsList();
-    for (PsiElement item : items) {
-      if (item instanceof PsiDirectory) {
-        item = ((PsiDirectory)item).findFile(PyNames.INIT_DOT_PY);
-      }
-      if (item instanceof PyFile) {
-        final PsiElement element = ((PyFile)item).getElementNamed(functionName);
-        if (element instanceof PyFunction) {
-          return (PyFunction)element;
-        }
-      }
-    }
-    return null;
-  }
-
-  @Nullable
-  private static NumPyDocString forFunction(@NotNull PyFunction function, @Nullable PsiElement reference, @Nullable String knownSignature) {
-    String docString = function.getDocStringValue();
-    if (docString == null && "__init__".equals(function.getName())) {
-      // Docstring for constructor can be found in the docstring of class
-      PyClass cls = function.getContainingClass();
-      if (cls != null) {
-        docString = cls.getDocStringValue();
-      }
-    }
-
-    if (docString != null) {
-      List<String> lines = splitByLines(docString);
-      dedent(lines);
-
-      String signature = null;
-      if (!lines.isEmpty() && SIGNATURE.matcher(lines.get(0)).matches()) {
-        signature = lines.get(0);
-        lines.remove(0);
-        dedent(lines);
-      }
-
-      String redirect = findRedirect(lines);
-      if (redirect != null && reference != null) {
-        PyFunction resolvedFunction = resolveRedirectToFunction(redirect, reference);
-        if (resolvedFunction != null) {
-          return forFunction(resolvedFunction, reference, knownSignature != null ? knownSignature : signature);
-        }
-      }
-      try {
-        return new NumPyDocString(knownSignature != null ? knownSignature : signature, lines);
-      }
-      catch (NotNumpyDocStringException e) {
-        return null;
-      }
-    }
-    return null;
-  }
-
-  /**
-   * Returns NumPyDocString object confirming to Numpy-style formatted docstring of specified function.
-   *
-   * @param function  Function containing docstring for which Numpy wrapper object is to be obtained.
-   * @param reference An original reference element to specified function.
-   * @return Numpy docstring wrapper object for specified function.
-   */
-  @Nullable
-  public static NumPyDocString forFunction(@NotNull PyFunction function, @Nullable PsiElement reference) {
-    return forFunction(function, reference, null);
-  }
-
-  @NotNull
-  private static List<String> splitByLines(@NotNull String text) {
-    List<String> lines = new ArrayList<String>();
-    for (String line : LINE_SEPARATOR.split(text)) {
-      if (!line.isEmpty() && !WHITE_SPACED_LINE.matcher(line).matches()) {
-        lines.add(line);
-      }
-    }
-    return lines;
-  }
-
-  private static void dedent(@NotNull List<String> lines) {
-    String margin = null;
-    for (String line : lines) {
-      Matcher matcher = ANY_INDENT.matcher(line);
-      if (matcher.find() && matcher.groupCount() != 0) {
-        String indent = matcher.group(1);
-        if (margin == null || (margin.startsWith(indent) && margin.length() != indent.length())) {
-          // update margin
-          margin = indent;
-        } else if (!indent.startsWith(margin)) {
-          // lines have no common margin
-          margin = "";
-          break;
-        }
-      }
-    }
-
-    if (margin != null && !margin.isEmpty()) {
-      for (int i = 0; i < lines.size(); i++) {
-        lines.set(i, lines.get(i).substring(margin.length()));
-      }
-    }
-  }
-
-  private static int indexOfMatch(@NotNull List<String> lines, @NotNull Pattern pattern, int start) {
-    for (int i = start; i < lines.size(); i++) {
-      if (pattern.matcher(lines.get(i)).matches()) {
-        return i;
-      }
-    }
-    return -1;
-  }
-
-  @NotNull
-  private static <T> List<T> copyOfRange(@NotNull List<T> src, int start, int end) {
-    List<T> dest = new ArrayList<T>();
-    if (start < 0) {
-      start = 0;
-    }
-    if (end < 0) {
-      end = src.size();
-    }
-    for (int i = start; i < end; i++) {
-      dest.add(src.get(i));
-    }
-    return dest;
-  }
-
-  @Nullable
-  private static String findRedirect(@NotNull List<String> lines) {
-    for (String line : lines) {
-      Matcher matcher = REDIRECT.matcher(line);
-      if (matcher.matches() && matcher.groupCount() > 0) {
-        return matcher.group(1);
-      }
-    }
-    return null;
-  }
-
-  private void parseSections(@NotNull List<String> lines) {
-    int current = indexOfMatch(lines, SECTION_HEADER, 1);
-    while (current != -1) {
-      int next = indexOfMatch(lines, SECTION_HEADER, current + 1);
-      String sectionName = lines.get(current - 1);
-      if ("Parameters".equalsIgnoreCase(sectionName)) {
-        parseParametersSection(copyOfRange(lines, current + 1, next - 1), myParameters);
-      } else if ("Returns".equalsIgnoreCase(sectionName)) {
-        parseParametersSection(copyOfRange(lines, current + 1, next - 1), myReturns);
-      }
-      current = next;
-    }
-  }
-
-  private static void parseParametersSection(@NotNull List<String> lines, List<NumPyDocStringParameter> parameters) {
-    DocStringParameterBuilder builder = null;
-    for (String line : lines) {
-      if (!HAS_INDENT.matcher(line).find()) {
-        builder = new DocStringParameterBuilder();
-        Matcher parameterWithTypeMatcher = PARAMETER_WITH_TYPE.matcher(line);
-        if (parameterWithTypeMatcher.matches()) {
-          builder.setName(parameterWithTypeMatcher.group(1));
-          builder.setType(parameterWithTypeMatcher.group(2));
-          parameters.add(builder.build());
-        } else {
-          Matcher parameterWithoutTypeMatcher = PARAMETER_WITHOUT_TYPE.matcher(line);
-          if (parameterWithoutTypeMatcher.matches()) {
-            builder.setName(parameterWithoutTypeMatcher.group(1));
-            builder.setType("object");
-            parameters.add(builder.build());
-          }
-          else {
-            builder.appendDescription(line.trim());
-          }
-        }
-      } else {
-        if (builder != null) {
-          builder.appendDescription(line.trim());
-        }
-      }
-    }
-  }
-
-  @Nullable
-  public static String cleanupOptional(@NotNull String typeString) {
-    int index = typeString.indexOf(", optional");
-    if (index >= 0) {
-      return typeString.substring(0, index);
-    }
-    return null;
-  }
-
-  @NotNull
-  public static List<String> getNumpyUnionType(@NotNull String typeString) {
-    final Matcher arrayMatcher = NUMPY_ARRAY_PATTERN.matcher(typeString);
-    if (arrayMatcher.matches()) {
-      typeString = arrayMatcher.group(2);
-    }
-    Matcher matcher = NUMPY_UNION_PATTERN.matcher(typeString);
-    if (matcher.matches()) {
-      typeString = matcher.group(1);
-    }
-    return Arrays.asList(typeString.split(" *, *"));
-  }
-
-  public static class NotNumpyDocStringException extends Exception {
-
-    public NotNumpyDocStringException(String signature) {
-      super("Function " + signature + " is not containing docstring of Numpy format.");
-    }
-  }
-
-  public static class DocStringParameterBuilder {
-    private String myName = "";
-    private String myType = "";
-    private StringBuilder myDescription = new StringBuilder();
-
-    public void setName(String name) {
-      myName = name;
-    }
-
-    public void setType(String type) {
-      myType = type;
-    }
-
-    public void appendDescription(String text) {
-      myDescription.append(" ");
-      myDescription.append(text);
-    }
-
-    public NumPyDocStringParameter build() {
-      return new NumPyDocStringParameter(myName, myType, myDescription.toString());
-    }
-  }
-}
index e19c3905788ad27778cff3401cf72e7f840108ef..ea613e0c91ebfd53640b0ee37ae25545190974b3 100644 (file)
@@ -342,7 +342,7 @@ public abstract class SectionBasedDocString extends DocStringLineParser implemen
   }
 
   @Nullable
-  private SectionField getFirstFieldForParameter(@NotNull final String name) {
+  public SectionField getFirstFieldForParameter(@NotNull final String name) {
     return ContainerUtil.find(getParameterFields(), new Condition<SectionField>() {
       @Override
       public boolean value(SectionField field) {