1
<?php
2
/**
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information please see
17
 * <http://phing.info>.
18
 */
19

20
/**
21
 * Base class for those classes that can appear inside the build file
22
 * as stand alone data types.
23
 *
24
 * This class handles the common description attribute and provides
25
 * a default implementation for reference handling and checking for
26
 * circular references that is appropriate for types that can not be
27
 * nested inside elements of the same type (i.e. patternset but not path)
28
 *
29
 * {@inheritdoc}
30
 *
31
 * @package phing.types
32
 */
33
class DataType extends ProjectComponent
34
{
35
    /**
36
     * Value to the refid attribute.
37
     *
38
     * @var Reference $ref
39
     */
40
    private $ref;
41

42
    /**
43
     * Are we sure we don't hold circular references?
44
     *
45
     * Subclasses are responsible for setting this value to false
46
     * if we'd need to investigate this condition (usually because a
47
     * child element has been added that is a subclass of DataType).
48
     *
49
     * @var boolean
50
     */
51
    protected $checked = true;
52

53
    /**
54
     * Has the refid attribute of this element been set?
55
     *
56
     * @return bool
57
     */
58 1
    public function isReference()
59
    {
60 1
        return ($this->ref !== null);
61
    }
62

63
    /**
64
     * @return string|null
65
     */
66 0
    public function getRefId()
67
    {
68 0
        return ($this->ref !== null ? $this->ref->getRefId() : null);
69
    }
70

71
    /**
72
     * Set the value of the refid attribute.
73
     *
74
     * Subclasses may need to check whether any other attributes
75
     * have been set as well or child elements have been created and
76
     * thus override this method. if they do they must call parent::setRefid()
77
     *
78
     * @param Reference $r
79
     *
80
     * @return void
81
     */
82 1
    public function setRefid(Reference $r)
83
    {
84 1
        $this->ref = $r;
85 1
        $this->checked = false;
86
    }
87

88
    /**
89
     * @param bool $checked
90
     */
91 1
    public function setChecked($checked)
92
    {
93 1
        $this->checked = $checked;
94
    }
95

96
    /**
97
     * Check to see whether any DataType we hold references to is
98
     * included in the Stack (which holds all DataType instances that
99
     * directly or indirectly reference this instance, including this
100
     * instance itself).
101
     *
102
     * If one is included, throw a BuildException created by circularReference
103
     *
104
     * This implementation is appropriate only for a DataType that
105
     * cannot hold other DataTypes as children.
106
     *
107
     * The general contract of this method is that it shouldn't do
108
     * anything if checked is true and set it to true on exit.
109
     *
110
     * @param $stk
111
     * @param Project $p
112
     *
113
     * @return void
114
     *
115
     * @throws BuildException
116
     */
117 1
    public function dieOnCircularReference(&$stk, Project $p = null)
118
    {
119 1
        if ($this->checked || !$this->isReference()) {
120 1
            return;
121
        }
122

123 1
        $o = $this->ref->getReferencedObject($p);
124

125 1
        if ($o instanceof DataType) {
126
            // TESTME - make sure that in_array() works just as well here
127
            //
128
            // check if reference is in stack
129
            //$contains = false;
130
            //for ($i=0, $size=count($stk); $i < $size; $i++) {
131
            //    if ($stk[$i] === $o) {
132
            //        $contains = true;
133
            //        break;
134
            //    }
135
            //}
136

137 1
            if (in_array($o, $stk, true)) {
138
                // throw build exception
139 1
                throw $this->circularReference();
140
            }
141

142 1
            $stk[] = $o;
143 1
            $o->dieOnCircularReference($stk, $p);
144 1
            array_pop($stk);
145
        }
146 1
        $this->checked = true;
147
    }
148

149 1
    public static function pushAndInvokeCircularReferenceCheck(DataType $dt, &$stk, Project $p)
150
    {
151 1
        $stk[] = $dt;
152 1
        $dt->dieOnCircularReference($stk, $p);
153 1
        array_pop($stk);
154
    }
155

156
    /**
157
     * Performs the check for circular references and returns the referenced object.
158
     *
159
     * @param $requiredClass
160
     * @param $dataTypeName
161
     *
162
     * @throws BuildException
163
     *
164
     * @return mixed
165
     */
166 1
    public function getCheckedRef($requiredClass, $dataTypeName)
167
    {
168 1
        if (!$this->checked) {
169
            // should be in stack
170 1
            $stk = [];
171 1
            $stk[] = $this;
172 1
            $this->dieOnCircularReference($stk, $this->getProject());
173
        }
174

175 1
        $o = $this->ref->getReferencedObject($this->getProject());
176 1
        if (!($o instanceof $requiredClass)) {
177 0
            throw new BuildException($this->ref->getRefId() . " doesn't denote a " . $dataTypeName);
178
        }
179

180 1
        return $o;
181
    }
182

183
    /**
184
     * Creates an exception that indicates that refid has to be the
185
     * only attribute if it is set.
186
     *
187
     * @return BuildException
188
     */
189 1
    public function tooManyAttributes()
190
    {
191 1
        return new BuildException("You must not specify more than one attribute when using refid");
192
    }
193

194
    /**
195
     * Creates an exception that indicates that this XML element must
196
     * not have child elements if the refid attribute is set.
197
     *
198
     * @return BuildException
199
     */
200 1
    public function noChildrenAllowed()
201
    {
202 1
        return new BuildException("You must not specify nested elements when using refid");
203
    }
204

205
    /**
206
     * Creates an exception that indicates the user has generated a
207
     * loop of data types referencing each other.
208
     *
209
     * @return BuildException
210
     */
211 1
    public function circularReference()
212
    {
213 1
        return new BuildException("This data type contains a circular reference.");
214
    }
215

216
    /**
217
     * Template method being called when the data type has been
218
     * parsed completely.
219
     *
220
     * {@inheritdoc}
221
     *
222
     * @return void
223
     */
224 0
    public function parsingComplete()
225
    {
226
    }
227

228
    /**
229
     * Gets as descriptive as possible a name used for this datatype instance.
230
     *
231
     * @return string name.
232
     */
233 1
    protected function getDataTypeName()
234
    {
235 1
        return ComponentHelper::getElementName($this->getProject(), $this, true);
236
    }
237

238
    /**
239
     * Basic DataType toString().
240
     *
241
     * @return string this DataType formatted as a String.
242
     */
243 1
    public function __toString()
244
    {
245 1
        $d = $this->getDescription();
246 1
        return $d === null ? $this->getDataTypeName() : $this->getDataTypeName() . " " . $d;
247
    }
248
}

Read our documentation on viewing source code .

Loading