Add ClipPathOperation for -webkit-clip-path organization
authorkrit@webkit.org <krit@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 16 Sep 2012 04:24:24 +0000 (04:24 +0000)
committerkrit@webkit.org <krit@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 16 Sep 2012 04:24:24 +0000 (04:24 +0000)
https://bugs.webkit.org/show_bug.cgi?id=95619

Reviewed by Dean Jackson.

This patch adds a new class ClipPathOperation to manage the values of the
-webkit-clip-path property. ClipPathOperation stores a Path object for clipping and is
a preparation for IRI references of the SVG 'clipPath' element.

The structure of ClipPathOperation is simular to FilterOperation.

ClipPathOperation will be extended to support IRI references directly in a second patch.

No new tests. The changes just affect the backend.

* GNUmakefile.list.am: Added new ClipPathOperation class.
* WebCore.gypi: Ditto.
* WebCore.vcproj/WebCore.vcproj: Ditto.
* WebCore.xcodeproj/project.pbxproj: Ditto.
* css/CSSComputedStyleDeclaration.cpp: Use ClipPathOperation instead of BasicShape.
(WebCore::CSSComputedStyleDeclaration::getPropertyCSSValue): Ditto.
* css/StyleBuilder.cpp: Ditto.
(WebCore):
(WebCore::ApplyPropertyClipPath::setValue):
(WebCore::ApplyPropertyClipPath::applyValue):
(WebCore::ApplyPropertyClipPath::createHandler):
* rendering/ClipPathOperation.h: Added. New handler for property values (BasicShape, references).
(WebCore):
(ClipPathOperation):
(WebCore::ClipPathOperation::~ClipPathOperation):
(WebCore::ClipPathOperation::operator!=):
(WebCore::ClipPathOperation::getOperationType): Return the operation type.
(WebCore::ClipPathOperation::isSameType): Helper function for =operator.
(WebCore::ClipPathOperation::ClipPathOperation):
(ShapeClipPathOperation): Inheriting class for managing BasicShapes.
(WebCore::ShapeClipPathOperation::create):
(WebCore::ShapeClipPathOperation::basicShape):
(WebCore::ShapeClipPathOperation::windRule):
(WebCore::ShapeClipPathOperation::path):
(WebCore::ShapeClipPathOperation::operator==):
(WebCore::ShapeClipPathOperation::ShapeClipPathOperation):
* rendering/RenderLayer.cpp: Use ClipPathOperation to apply clipping.
(WebCore::RenderLayer::paintLayerContents):
* rendering/style/RenderStyle.h:
* rendering/style/StyleRareNonInheritedData.h:
(StyleRareNonInheritedData):
* rendering/svg/SVGRenderingContext.cpp: Ditto.
(WebCore::SVGRenderingContext::prepareToRenderSVGContent):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@128700 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
Source/WebCore/ChangeLog
Source/WebCore/GNUmakefile.list.am
Source/WebCore/WebCore.gypi
Source/WebCore/WebCore.vcproj/WebCore.vcproj
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/css/CSSComputedStyleDeclaration.cpp
Source/WebCore/css/StyleBuilder.cpp
Source/WebCore/rendering/ClipPathOperation.h [new file with mode: 0644]
Source/WebCore/rendering/RenderLayer.cpp
Source/WebCore/rendering/style/RenderStyle.h
Source/WebCore/rendering/style/StyleRareNonInheritedData.h
Source/WebCore/rendering/svg/SVGRenderingContext.cpp

index 5672df1..23c84e7 100644 (file)
@@ -1,3 +1,54 @@
+2012-09-11  Dirk Schulze  <krit@webkit.org>
+
+        Add ClipPathOperation for -webkit-clip-path organization
+        https://bugs.webkit.org/show_bug.cgi?id=95619
+
+        Reviewed by Dean Jackson.
+
+        This patch adds a new class ClipPathOperation to manage the values of the
+        -webkit-clip-path property. ClipPathOperation stores a Path object for clipping and is
+        a preparation for IRI references of the SVG 'clipPath' element.
+
+        The structure of ClipPathOperation is simular to FilterOperation.
+
+        ClipPathOperation will be extended to support IRI references directly in a second patch.
+
+        No new tests. The changes just affect the backend.
+
+        * GNUmakefile.list.am: Added new ClipPathOperation class.
+        * WebCore.gypi: Ditto.
+        * WebCore.vcproj/WebCore.vcproj: Ditto.
+        * WebCore.xcodeproj/project.pbxproj: Ditto.
+        * css/CSSComputedStyleDeclaration.cpp: Use ClipPathOperation instead of BasicShape.
+        (WebCore::CSSComputedStyleDeclaration::getPropertyCSSValue): Ditto.
+        * css/StyleBuilder.cpp: Ditto.
+        (WebCore):
+        (WebCore::ApplyPropertyClipPath::setValue):
+        (WebCore::ApplyPropertyClipPath::applyValue):
+        (WebCore::ApplyPropertyClipPath::createHandler):
+        * rendering/ClipPathOperation.h: Added. New handler for property values (BasicShape, references).
+        (WebCore):
+        (ClipPathOperation):
+        (WebCore::ClipPathOperation::~ClipPathOperation):
+        (WebCore::ClipPathOperation::operator!=):
+        (WebCore::ClipPathOperation::getOperationType): Return the operation type.
+        (WebCore::ClipPathOperation::isSameType): Helper function for =operator.
+        (WebCore::ClipPathOperation::ClipPathOperation):
+        (ShapeClipPathOperation): Inheriting class for managing BasicShapes.
+        (WebCore::ShapeClipPathOperation::create):
+        (WebCore::ShapeClipPathOperation::basicShape):
+        (WebCore::ShapeClipPathOperation::windRule):
+        (WebCore::ShapeClipPathOperation::path):
+        (WebCore::ShapeClipPathOperation::operator==):
+        (WebCore::ShapeClipPathOperation::ShapeClipPathOperation):
+        * rendering/RenderLayer.cpp: Use ClipPathOperation to apply clipping.
+        (WebCore::RenderLayer::paintLayerContents):
+        * rendering/style/RenderStyle.h:
+        * rendering/style/StyleRareNonInheritedData.h:
+        (StyleRareNonInheritedData):
+        * rendering/svg/SVGRenderingContext.cpp: Ditto.
+        (WebCore::SVGRenderingContext::prepareToRenderSVGContent):
+
 2012-09-15  Andreas Kling  <kling@webkit.org>
 
         REGRESSION(r127438): Google Docs to renders text too small.
index e937d12..1f24b73 100644 (file)
@@ -5117,6 +5117,7 @@ webcore_sources += \
        Source/WebCore/rendering/style/BasicShapes.h \
        Source/WebCore/rendering/style/BorderData.h \
        Source/WebCore/rendering/style/BorderValue.h \
+       Source/WebCore/rendering/style/ClipPathOperation.h \
        Source/WebCore/rendering/style/CollapsedBorderValue.h \
        Source/WebCore/rendering/style/ContentData.cpp \
        Source/WebCore/rendering/style/ContentData.h \
index 4085311..aefcdac 100644 (file)
             'rendering/style/BasicShapes.h',
             'rendering/style/BorderData.h',
             'rendering/style/BorderValue.h',
+            'rendering/style/ClipPathOperation.h',
             'rendering/style/CollapsedBorderValue.h',
             'rendering/style/ContentData.h',
             'rendering/style/CounterContent.h',
index eec6b5e..06274b9 100755 (executable)
                                        RelativePath="..\rendering\style\BasicShapes.h"
                                        >
                                </File>
+                               <File
+                                       RelativePath="..\rendering\style\ClipPathOperation.h"
+                                       >
+                               </File>
                        </Filter>
                        <Filter
                                Name="svg"
index cccb97a..a5d6d74 100644 (file)
                FABE72FD1059C21100D999DD /* MathMLElementFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FABE72FB1059C21100D999DD /* MathMLElementFactory.cpp */; };
                FABE72FE1059C21100D999DD /* MathMLNames.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FABE72FC1059C21100D999DD /* MathMLNames.cpp */; };
                FB78AD2E151BF5E600FE54D3 /* CSSParserMode.h in Headers */ = {isa = PBXBuildFile; fileRef = FB78AD2C151BF5D200FE54D3 /* CSSParserMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               FB92DF4B15FED08700994433 /* ClipPathOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = FB92DF4915FED08700994433 /* ClipPathOperation.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FBC220DF1237FBEB00BCF788 /* GraphicsContext3DOpenGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FBC220DE1237FBEB00BCF788 /* GraphicsContext3DOpenGL.cpp */; };
                FBD6AF8815EF25C9008B7110 /* CSSBasicShapes.h in Headers */ = {isa = PBXBuildFile; fileRef = FBD6AF8715EF21D4008B7110 /* CSSBasicShapes.h */; };
                FBD6AF8915EF25DB008B7110 /* BasicShapeFunctions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FBD6AF8415EF21D4008B7110 /* BasicShapeFunctions.cpp */; };
                FABE72FB1059C21100D999DD /* MathMLElementFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MathMLElementFactory.cpp; sourceTree = "<group>"; };
                FABE72FC1059C21100D999DD /* MathMLNames.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MathMLNames.cpp; sourceTree = "<group>"; };
                FB78AD2C151BF5D200FE54D3 /* CSSParserMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSParserMode.h; sourceTree = "<group>"; };
+               FB92DF4915FED08700994433 /* ClipPathOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ClipPathOperation.h; sourceTree = "<group>"; };
                FBC220DE1237FBEB00BCF788 /* GraphicsContext3DOpenGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GraphicsContext3DOpenGL.cpp; sourceTree = "<group>"; };
                FBD6AF8215EF21A3008B7110 /* BasicShapes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BasicShapes.cpp; path = style/BasicShapes.cpp; sourceTree = "<group>"; };
                FBD6AF8315EF21A3008B7110 /* BasicShapes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BasicShapes.h; path = style/BasicShapes.h; sourceTree = "<group>"; };
                                FBD6AF8315EF21A3008B7110 /* BasicShapes.h */,
                                BC5EB5E00E81BE8700B25965 /* BorderData.h */,
                                BC5EB5DA0E81B7EA00B25965 /* BorderValue.h */,
+                               FB92DF4915FED08700994433 /* ClipPathOperation.h */,
                                BC5EB5DE0E81B9AB00B25965 /* CollapsedBorderValue.h */,
                                BC5EB97E0E82072500B25965 /* ContentData.cpp */,
                                BC5EB97F0E82072500B25965 /* ContentData.h */,
                                E1BE512E0CF6C512002EA959 /* XSLTUnicodeSort.h in Headers */,
                                977E2E0F12F0FC9C00C13379 /* XSSAuditor.h in Headers */,
                                FD537353137B651800008DCE /* ZeroPole.h in Headers */,
+                               FB92DF4B15FED08700994433 /* ClipPathOperation.h in Headers */,
                                4F7B4A6615FF6D6A006B5F22 /* PlatformMemoryInstrumentation.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
index 68c8448..460cc32 100644 (file)
@@ -2417,9 +2417,11 @@ PassRefPtr<CSSValue> CSSComputedStyleDeclaration::getPropertyCSSValue(CSSPropert
         case CSSPropertyCounterReset:
             return counterToCSSValue(style.get(), propertyID);
         case CSSPropertyWebkitClipPath:
-            if (!style->clipPath())
-                return cssValuePool().createIdentifierValue(CSSValueNone);
-            return valueForBasicShape(style->clipPath());
+            if (ClipPathOperation* operation = style->clipPath()) {
+                if (operation->getOperationType() == ClipPathOperation::SHAPE)
+                    return valueForBasicShape(static_cast<ShapeClipPathOperation*>(operation)->basicShape());
+            }
+            return cssValuePool().createIdentifierValue(CSSValueNone);
 #if ENABLE(CSS_REGIONS)
         case CSSPropertyWebkitFlowInto:
             if (style->flowThread().isNull())
index e5931a3..09c27b7 100644 (file)
@@ -33,6 +33,7 @@
 #include "CSSPrimitiveValueMappings.h"
 #include "CSSToStyleMap.h"
 #include "CSSValueList.h"
+#include "ClipPathOperation.h"
 #include "CursorList.h"
 #include "Document.h"
 #include "Element.h"
@@ -1684,10 +1685,10 @@ public:
     }
 };
 
-template <BasicShape* (RenderStyle::*getterFunction)() const, void (RenderStyle::*setterFunction)(PassRefPtr<BasicShape>), BasicShape* (*initialFunction)()>
+template <ClipPathOperation* (RenderStyle::*getterFunction)() const, void (RenderStyle::*setterFunction)(PassRefPtr<ClipPathOperation>), ClipPathOperation* (*initialFunction)()>
 class ApplyPropertyClipPath {
 public:
-    static void setValue(RenderStyle* style, PassRefPtr<BasicShape> value) { (style->*setterFunction)(value); }
+    static void setValue(RenderStyle* style, PassRefPtr<ClipPathOperation> value) { (style->*setterFunction)(value); }
     static void applyValue(CSSPropertyID, StyleResolver* styleResolver, CSSValue* value)
     {
         if (value->isPrimitiveValue()) {
@@ -1695,14 +1696,13 @@ public:
             if (primitiveValue->getIdent() == CSSValueNone)
                 setValue(styleResolver->style(), 0);
             else if (primitiveValue->isShape()) {
-                RefPtr<BasicShape> clipPathShape = basicShapeForValue(styleResolver, primitiveValue->getShapeValue());
-                setValue(styleResolver->style(), clipPathShape.release());
+                setValue(styleResolver->style(), ShapeClipPathOperation::create(basicShapeForValue(styleResolver, primitiveValue->getShapeValue())));
             }
         }
     }
     static PropertyHandler createHandler()
     {
-        PropertyHandler handler = ApplyPropertyDefaultBase<BasicShape*, getterFunction, PassRefPtr<BasicShape>, setterFunction, BasicShape*, initialFunction>::createHandler();
+        PropertyHandler handler = ApplyPropertyDefaultBase<ClipPathOperation*, getterFunction, PassRefPtr<ClipPathOperation>, setterFunction, ClipPathOperation*, initialFunction>::createHandler();
         return PropertyHandler(handler.inheritFunction(), handler.initialFunction(), &applyValue);
     }
 };
diff --git a/Source/WebCore/rendering/ClipPathOperation.h b/Source/WebCore/rendering/ClipPathOperation.h
new file mode 100644 (file)
index 0000000..c469d6d
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef ClipPathOperation_h
+#define ClipPathOperation_h
+
+#include "BasicShapes.h"
+#include "Path.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class ClipPathOperation : public RefCounted<ClipPathOperation> {
+public:
+    enum OperationType {
+        // FIXME: Add referencing for IRI https://bugs.webkit.org/show_bug.cgi?id=96381
+        SHAPE
+    };
+
+    virtual ~ClipPathOperation() { }
+
+    virtual bool operator==(const ClipPathOperation&) const = 0;
+    bool operator!=(const ClipPathOperation& o) const { return !(*this == o); }
+
+    virtual OperationType getOperationType() const { return m_type; }
+    virtual bool isSameType(const ClipPathOperation& o) const { return o.getOperationType() == m_type; }
+
+protected:
+    ClipPathOperation(OperationType type)
+        : m_type(type)
+    {
+    }
+
+    OperationType m_type;
+};
+
+class ShapeClipPathOperation : public ClipPathOperation {
+public:
+    static PassRefPtr<ShapeClipPathOperation> create(PassRefPtr<BasicShape> shape)
+    {
+        return adoptRef(new ShapeClipPathOperation(shape));
+    }
+
+    const BasicShape* basicShape() const { return m_shape.get(); }
+    WindRule windRule() const { return m_shape->windRule(); }
+    const Path& path(const FloatRect& boundingRect)
+    {
+        ASSERT(m_shape);
+        m_path.clear();
+        m_path = adoptPtr(new Path);
+        m_shape->path(*m_path, boundingRect);
+        return *m_path;
+    }
+
+private:
+    virtual bool operator==(const ClipPathOperation& o) const
+    {
+        if (!isSameType(o))
+            return false;
+        const ShapeClipPathOperation* other = static_cast<const ShapeClipPathOperation*>(&o);
+        return m_shape == other->m_shape;
+    }
+
+    ShapeClipPathOperation(PassRefPtr<BasicShape> shape)
+        : ClipPathOperation(SHAPE)
+        , m_shape(shape)
+    {
+    }
+
+    RefPtr<BasicShape> m_shape;
+    OwnPtr<Path> m_path;
+};
+}
+
+#endif // ClipPathOperation_h
index 02e1f17..a49544d 100644 (file)
@@ -3116,12 +3116,10 @@ void RenderLayer::paintLayerContents(RenderLayer* rootLayer, GraphicsContext* co
     // Apply clip-path to context.
     RenderStyle* style = renderer()->style();
     if (renderer()->hasClipPath() && !context->paintingDisabled() && style) {
-        if (BasicShape* clipShape = style->clipPath()) {
-            // FIXME: Investigate if it is better to store and update a Path object in RenderStyle.
-            // https://bugs.webkit.org/show_bug.cgi?id=95619
-            Path clipPath;
-            clipShape->path(clipPath, calculateLayerBounds(this, rootLayer, 0));
-            transparencyLayerContext->clipPath(clipPath, clipShape->windRule());
+        ASSERT(style->clipPath());
+        if (style->clipPath()->getOperationType() == ClipPathOperation::SHAPE) {
+            ShapeClipPathOperation* clipPath = static_cast<ShapeClipPathOperation*>(style->clipPath());
+            transparencyLayerContext->clipPath(clipPath->path(calculateLayerBounds(this, rootLayer, 0)), clipPath->windRule());
         }
     }
 
index 7a11744..7bcb38e 100644 (file)
@@ -1462,14 +1462,14 @@ public:
     static BasicShape* initialWrapShapeInside() { return 0; }
     static BasicShape* initialWrapShapeOutside() { return 0; }
 
-    void setClipPath(PassRefPtr<BasicShape> shape)
+    void setClipPath(PassRefPtr<ClipPathOperation> operation)
     {
-        if (rareNonInheritedData->m_clipPath != shape)
-            rareNonInheritedData.access()->m_clipPath = shape;
+        if (rareNonInheritedData->m_clipPath != operation)
+            rareNonInheritedData.access()->m_clipPath = operation;
     }
-    BasicShape* clipPath() const { return rareNonInheritedData->m_clipPath.get(); }
+    ClipPathOperation* clipPath() const { return rareNonInheritedData->m_clipPath.get(); }
 
-    static BasicShape* initialClipPath() { return 0; }
+    static ClipPathOperation* initialClipPath() { return 0; }
 
     Length wrapPadding() const { return rareNonInheritedData->m_wrapPadding; }
     void setWrapPadding(Length wrapPadding) { SET_VAR(rareNonInheritedData, m_wrapPadding, wrapPadding); }
index bb67a3e..41b8407 100644 (file)
@@ -26,6 +26,7 @@
 #define StyleRareNonInheritedData_h
 
 #include "BasicShapes.h"
+#include "ClipPathOperation.h"
 #include "CounterDirectives.h"
 #include "CursorData.h"
 #include "DataRef.h"
@@ -138,7 +139,7 @@ public:
     Length m_wrapMargin;
     Length m_wrapPadding;
 
-    RefPtr<BasicShape> m_clipPath;
+    RefPtr<ClipPathOperation> m_clipPath;
 
     Color m_visitedLinkBackgroundColor;
     Color m_visitedLinkOutlineColor;
index 6e3cd2d..041aed4 100644 (file)
@@ -123,13 +123,10 @@ void SVGRenderingContext::prepareToRenderSVGContent(RenderObject* object, PaintI
         }
     }
 
-    BasicShape* clipShape = style->clipPath();
-    if (clipShape) {
-        // FIXME: Investigate if it is better to store and update a Path object in RenderStyle.
-        // https://bugs.webkit.org/show_bug.cgi?id=95619
-        Path clipPath;
-        clipShape->path(clipPath, object->objectBoundingBox());
-        m_paintInfo->context->clipPath(clipPath, clipShape->windRule());
+    ClipPathOperation* clipPathOperation = style->clipPath();
+    if (clipPathOperation && clipPathOperation->getOperationType() == ClipPathOperation::SHAPE) {
+        ShapeClipPathOperation* clipPath = static_cast<ShapeClipPathOperation*>(clipPathOperation);
+        m_paintInfo->context->clipPath(clipPath->path(object->objectBoundingBox()), clipPath->windRule());
     }
 
     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(m_object);
@@ -150,7 +147,7 @@ void SVGRenderingContext::prepareToRenderSVGContent(RenderObject* object, PaintI
     }
 
     RenderSVGResourceClipper* clipper = resources->clipper();
-    if (!clipShape && clipper) {
+    if (!clipPathOperation && clipper) {
         if (!clipper->applyResource(m_object, style, m_paintInfo->context, ApplyToDefaultMode))
             return;
     }