1##############################################################################
2#
3# Copyright (c) 2003 Zope Foundation and Contributors.
4# All Rights Reserved.
5#
6# This software is subject to the provisions of the Zope Public License,
7# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11# FOR A PARTICULAR PURPOSE.
12#
13##############################################################################
14""" Utilities for the 'nested directive' section in the narrative docs.
15"""
16
17from zope.interface import Attribute
18from zope.interface import Interface
19from zope.interface import implementer
20from zope.schema import BytesLine
21from zope.schema import Id
22from zope.schema import Int
23from zope.schema import Text
24from zope.schema import TextLine
25from zope.configuration.config import GroupingContextDecorator
26from zope.configuration.config import IConfigurationContext
27from zope.configuration.fields import Bool
28from zope.configuration._compat import u
29
30
31schema_registry = {}
32
33class ISchemaInfo(Interface):
34    """Parameter schema for the schema directive
35    """
36
37    name = TextLine(
38        title=u("The schema name"),
39        description=u("This is a descriptive name for the schema."),
40        )
41
42    id = Id(title=u("The unique id for the schema"))
43
44class ISchema(Interface):
45    """Interface that distinguishes the schema directive
46    """
47
48    fields = Attribute("Dictionary of field definitions")
49
50
51@implementer(IConfigurationContext, ISchema)
52class Schema(GroupingContextDecorator):
53    """Handle schema directives
54    """
55
56
57    def __init__(self, context, name, id):
58        self.context, self.name, self.id = context, name, id
59        self.fields = {}
60
61    def after(self):
62        schema = Interface.__class__(
63            self.name,
64            (Interface, ),
65            self.fields
66            )
67        schema.__doc__ = self.info.text.strip()
68        self.action(
69            discriminator=('schema', self.id),
70            callable=schema_registry.__setitem__,
71            args=(self.id, schema),
72            )
73
74
75class IFieldInfo(Interface):
76
77    name = BytesLine(
78        title=u("The field name"),
79        )
80
81    title = TextLine(
82        title=u("Title"),
83        description=u("A short summary or label"),
84        default=u(""),
85        required=False,
86        )
87
88    required = Bool(
89        title=u("Required"),
90        description=u("Determines whether a value is required."),
91        default=True)
92
93    readonly = Bool(
94        title=u("Read Only"),
95        description=u("Can the value be modified?"),
96        required=False,
97        default=False)
98
99class ITextInfo(IFieldInfo):
100
101    min_length = Int(
102        title=u("Minimum length"),
103        description=u("Value after whitespace processing cannot have less than "
104                      "min_length characters. If min_length is None, there is "
105                      "no minimum."),
106        required=False,
107        min=0, # needs to be a positive number
108        default=0)
109
110    max_length = Int(
111        title=u("Maximum length"),
112        description=u("Value after whitespace processing cannot have greater "
113                      "or equal than max_length characters. If max_length is "
114                      "None, there is no maximum."),
115        required=False,
116        min=0, # needs to be a positive number
117        default=None)
118
119def field(context, constructor, name, **kw):
120
121    # Compute the field
122    field = constructor(description=context.info.text.strip(), **kw)
123
124    # Save it in the schema's field dictionary
125    schema = context.context
126    if name in schema.fields:
127        raise ValueError("Duplicate field", name)
128    schema.fields[name] = field
129
130
131def textField(context, **kw):
132    field(context, Text, **kw)
133
134class IIntInfo(IFieldInfo):
135
136    min = Int(
137        title=u("Start of the range"),
138        required=False,
139        default=None
140        )
141
142    max = Int(
143        title=u("End of the range (excluding the value itself)"),
144        required=False,
145        default=None
146        )
147
148def intField(context, **kw):
149    field(context, Int, **kw)
150