DoWhatIMeanSupport.st
changeset 5035 5afe663f6f8d
parent 5033 743f882894d9
child 5037 a991ac280740
child 5038 137974a28198
equal deleted inserted replaced
5033:743f882894d9 5035:5afe663f6f8d
  1701 
  1701 
  1702     "Created: / 10-11-2006 / 14:00:53 / cg"
  1702     "Created: / 10-11-2006 / 14:00:53 / cg"
  1703 !
  1703 !
  1704 
  1704 
  1705 classOfNode:aNode
  1705 classOfNode:aNode
  1706     "when showing possible completions for a message,
  1706     "returns the class of a receiver, if it is well-known.
       
  1707      Otherwise nil (either unknown, or multiple possibilities)
       
  1708      When showing possible completions for a message,
  1707      it is a good idea to know what the kind receiver is."
  1709      it is a good idea to know what the kind receiver is."
  1708 
  1710 
  1709     | nm nodeVal receiverClass nodeSelector nodeReceiver mthd|
  1711     | classes |
       
  1712 
       
  1713     classes := self classesOfNode:aNode.
       
  1714     classes size == 1 ifTrue:[
       
  1715         ^ classes anElement
       
  1716     ].
       
  1717     ^ nil
       
  1718 !
       
  1719 
       
  1720 classesOfInstVarNamed:varName inClass:aClass
       
  1721     |setOfTypes instIndex|
       
  1722     
       
  1723     setOfTypes := IdentitySet new.
       
  1724     instIndex := aClass instVarIndexFor:varName.
       
  1725 
       
  1726     "/ look for instances
       
  1727     aClass allSubInstancesDo:[:i |
       
  1728         |varClass|
       
  1729         varClass := (i instVarAt:instIndex) class.
       
  1730         setOfTypes add:varClass.
       
  1731     ].  
       
  1732     
       
  1733     "/ look for assignments in code
       
  1734     aClass withAllSubclassesDo:[:eachClass |
       
  1735         eachClass methodDictionary do:[:m |
       
  1736             |tree code visitor|
       
  1737 
       
  1738             "/ quick check
       
  1739             code := m source.
       
  1740             (code notNil and:[code includesString:varName]) ifTrue:[
       
  1741                 tree := Parser parse:code class:eachClass.
       
  1742                 (tree notNil and:[tree ~~ #Error]) ifTrue:[
       
  1743                     visitor := PluggableParseNodeVisitor new. 
       
  1744                     visitor 
       
  1745                         actionForNodeClass:AssignmentNode 
       
  1746                         put:[:node |
       
  1747                             |val|
       
  1748 
       
  1749                             node variable name = varName ifTrue:[
       
  1750                                 "/ only look for wellknown types on the right side.
       
  1751                                 node expression isConstant ifTrue:[
       
  1752                                     val := node expression evaluate.
       
  1753                                     val isArray ifTrue:[
       
  1754                                         setOfTypes add:Array 
       
  1755                                     ] ifFalse:[
       
  1756                                         setOfTypes add:val class
       
  1757                                     ].
       
  1758                                 ] ifFalse:[
       
  1759                                     node expression isMessage ifTrue:[
       
  1760                                         ( #(+ - * /) includes:node expression selector ) ifTrue:[
       
  1761                                             setOfTypes add:Number
       
  1762                                         ] ifFalse:[    
       
  1763                                             ( #(// size) includes:node expression selector ) ifTrue:[
       
  1764                                                 setOfTypes add:Integer
       
  1765                                             ] ifFalse:[    
       
  1766                                                 ( #(copy shallowCopy) includes:node expression selector ) ifTrue:[
       
  1767                                                 ] ifFalse:[    
       
  1768                                                     ( #(new new: basicNew basicNew:) includes:node expression selector ) ifTrue:[
       
  1769                                                         node expression receiver isGlobal ifTrue:[
       
  1770                                                             setOfTypes add:node expression receiver evaluate
       
  1771                                                         ].    
       
  1772                                                     ] ifFalse:[    
       
  1773 self breakPoint:#cg.
       
  1774                                                     ]
       
  1775                                                 ]
       
  1776                                             ]
       
  1777                                         ]
       
  1778                                     ].    
       
  1779                                 ].    
       
  1780                             ].
       
  1781                             true "/ yes - visit subnodes
       
  1782                         ].        
       
  1783                     visitor visit:tree.
       
  1784                 ].    
       
  1785             ]    
       
  1786         ]
       
  1787     ].
       
  1788     ^ setOfTypes
       
  1789 !
       
  1790 
       
  1791 classesOfNode:aNode
       
  1792     "returns the set of possible classes of a receiver.
       
  1793      or nil if unknown.
       
  1794      When showing possible completions for a message,
       
  1795      it is a good idea to know what the kind receiver is."
       
  1796 
       
  1797     | nm nodeVal receiverClass nodeSelector nodeReceiver mthd instVarClass|
  1710 
  1798 
  1711     aNode isBlock ifTrue:[
  1799     aNode isBlock ifTrue:[
  1712         ^ Block
  1800         ^ { Block }
  1713     ].
  1801     ].
  1714 
       
  1715     (nodeVal := self valueOfNode:aNode) notNil ifTrue:[
  1802     (nodeVal := self valueOfNode:aNode) notNil ifTrue:[
  1716         "/ knowing the value is always great!!
  1803         "/ knowing the value is always great!!
  1717         ^ nodeVal class
  1804         ^ { nodeVal class }
  1718     ].
  1805     ].
  1719 
  1806 
  1720     aNode isVariable ifTrue:[
  1807     aNode isVariable ifTrue:[
  1721         nm := aNode name.
  1808         nm := aNode name.
  1722         nm = 'self' ifTrue:[
  1809         nm = 'self' ifTrue:[
  1723             classOrNil isNil ifTrue:[^ UndefinedObject].
  1810             classOrNil isNil ifTrue:[^ { UndefinedObject } ].
  1724             ^ classOrNil
  1811             ^ { classOrNil }
  1725         ].
  1812         ].
  1726         nm = 'super' ifTrue:[
  1813         nm = 'super' ifTrue:[
  1727             classOrNil isNil ifTrue:[^ Object].
  1814             classOrNil isNil ifTrue:[^ Object].
  1728             ^ classOrNil superclass
  1815             ^ { classOrNil superclass }
  1729         ].
  1816         ].
  1730         nm = 'thisContext' ifTrue:[
  1817         nm = 'thisContext' ifTrue:[
  1731             ^ Context
  1818             ^ { Context }
  1732         ].
  1819         ].
  1733 
  1820 
  1734 "/        classOrNil notNil ifTrue:[
  1821         classOrNil notNil ifTrue:[
       
  1822             instVarClass := classOrNil whichClassDefinesInstVar:nm.
       
  1823             instVarClass notNil ifTrue:[
       
  1824                 ^ self classesOfInstVarNamed:nm inClass:instVarClass.
       
  1825             ].    
  1735 "/            (classOrNil allInstVarNames includes:nm) ifTrue:[
  1826 "/            (classOrNil allInstVarNames includes:nm) ifTrue:[
  1736 "/                "/ could look at existing instances here...
  1827 "/                "/ could look at existing instances here...
  1737 "/                self breakPoint:#cg.
  1828 "/                self breakPoint:#cg.
  1738 "/            ].
  1829 "/            ].
  1739 "/        ].
  1830         ].
  1740         ^ nil
  1831         ^ nil
  1741     ].
  1832     ].
  1742 
  1833 
  1743     aNode isMessage ifTrue:[
  1834     aNode isMessage ifTrue:[
  1744         nodeSelector := aNode selector.
  1835         nodeSelector := aNode selector.
  1746 
  1837 
  1747         "/ some hardwired knowlegde here
  1838         "/ some hardwired knowlegde here
  1748         receiverClass := self classOfNode:nodeReceiver.
  1839         receiverClass := self classOfNode:nodeReceiver.
  1749         receiverClass notNil ifTrue:[
  1840         receiverClass notNil ifTrue:[
  1750             nodeSelector == #class ifTrue:[
  1841             nodeSelector == #class ifTrue:[
  1751                 ^ receiverClass class
  1842                 ^ { receiverClass class }
  1752             ].
  1843             ].
  1753 
  1844 
  1754             receiverClass isBehavior ifTrue:[
  1845             receiverClass isBehavior ifTrue:[
  1755                 mthd := receiverClass lookupMethodFor:nodeSelector.
  1846                 mthd := receiverClass lookupMethodFor:nodeSelector.
  1756                 receiverClass isMeta ifTrue:[
  1847                 receiverClass isMeta ifTrue:[
  1757                     ( #( #'new' #'basicNew' #'new:' #'basicNew:' #'with:' #'with:with:') includes: nodeSelector ) ifTrue:[
  1848                     ( #( #'new' #'basicNew' #'new:' #'basicNew:' #'with:' #'with:with:') includes: nodeSelector ) ifTrue:[
  1758                         ^ receiverClass theNonMetaclass
  1849                         ^ { receiverClass theNonMetaclass }
  1759                     ].
  1850                     ].
  1760                     "/ if that method sends one of new/basicNew/new:/basicNew:, assume it returns an instance of itself
  1851                     "/ if that method sends one of new/basicNew/new:/basicNew:, assume it returns an instance of itself
  1761                     mthd notNil ifTrue:[
  1852                     mthd notNil ifTrue:[
  1762                         ( mthd sendsAny:#( #'new' #'basicNew' #'new:' #'basicNew:' )) ifTrue:[
  1853                         ( mthd sendsAny:#( #'new' #'basicNew' #'new:' #'basicNew:' )) ifTrue:[
  1763                             ^ receiverClass theNonMetaclass
  1854                             ^ { receiverClass theNonMetaclass }
  1764                         ].
  1855                         ].
  1765                     ].
  1856                     ].
  1766                 ] ifFalse:[
  1857                 ] ifFalse:[
  1767                     mthd notNil ifTrue:[
  1858                     mthd notNil ifTrue:[
  1768                         (ParseTreeSearcher methodIsSetterMethod:mthd) ifTrue:[
  1859                         (ParseTreeSearcher methodIsSetterMethod:mthd) ifTrue:[
  1769                             ^ receiverClass.
  1860                             ^ { receiverClass }.
  1770                         ]
  1861                         ]
  1771                     ]
  1862                     ]
  1772                 ]
  1863                 ]
  1773             ].
  1864             ].
  1774         ].
  1865         ].
  1775         classOrNil notNil ifTrue:[
  1866         classOrNil notNil ifTrue:[
  1776             (nodeReceiver isSelf and:[nodeSelector = #'class']) ifTrue:[
  1867             (nodeReceiver isSelf and:[nodeSelector = #'class']) ifTrue:[
  1777                 ^ classOrNil class
  1868                 ^ { classOrNil class }
  1778             ].
  1869             ].
  1779         ].
  1870         ].
  1780 
  1871 
  1781         (nodeSelector = #'asFilename') ifTrue:[
  1872         (nodeSelector = #'asFilename') ifTrue:[
  1782             ^ Filename
  1873             ^ { Filename }
  1783         ].
  1874         ].
  1784         (nodeSelector = #'asOrderedCollection') ifTrue:[
  1875         (nodeSelector = #'asOrderedCollection') ifTrue:[
  1785             ^ OrderedCollection
  1876             ^ { OrderedCollection }
  1786         ].
  1877         ].
  1787         (nodeSelector = #'asArray') ifTrue:[
  1878         (nodeSelector = #'asArray') ifTrue:[
  1788             ^ Array
  1879             ^ { Array }
  1789         ].
  1880         ].
  1790         (nodeSelector = #'asSet') ifTrue:[
  1881         (nodeSelector = #'asSet') ifTrue:[
  1791             ^ Set
  1882             ^ { Set }
  1792         ].
  1883         ].
  1793         (nodeSelector = #'size') ifTrue:[
  1884         (nodeSelector = #'size') ifTrue:[
  1794             ^ SmallInteger
  1885             ^ { SmallInteger }
  1795         ].
  1886         ].
  1796 
  1887 
  1797         "/ some wellknown boolean returners (need better type inference here)
  1888         "/ some wellknown boolean returners (need better type inference here)
  1798         (#( isNil notNil not isEmptyOrNil notEmptyOrNil notEmpty isEmpty
  1889         (#( isNil notNil not isEmptyOrNil notEmptyOrNil notEmpty isEmpty
  1799             isBehavior isMeta
  1890             isBehavior isMeta
  1800             = ~= == ~~ > >= < <=
  1891             = ~= == ~~ > >= < <=
  1801             includes: contains:
  1892             includes: contains:
  1802             and: or:
  1893             and: or:
  1803             exists atEnd
  1894             exists atEnd
  1804         ) includes:nodeSelector ) ifTrue:[
  1895         ) includes:nodeSelector ) ifTrue:[
  1805             ^ True "/ Boolean - not boolean; it does not contain the full protocol (would not find ifTrue:)
  1896             ^ { True } "/ Boolean - not boolean; it does not contain the full protocol (would not find ifTrue:)
  1806         ].
  1897         ].
  1807 
  1898 
  1808         ( #( + - * / // \\ ) includes:nodeSelector) ifTrue:[
  1899         ( #( + - * / // \\ ) includes:nodeSelector) ifTrue:[
  1809             "/ assume numeric
  1900             "/ assume numeric
  1810             ^ Number
  1901             ^ { Number }
  1811         ].
  1902         ].
  1812 
  1903 
  1813         ( #( class theMetaclass theNonMetaclass ) includes:nodeSelector) ifTrue:[
  1904         ( #( class theMetaclass theNonMetaclass ) includes:nodeSelector) ifTrue:[
  1814             "/ assume behavior
  1905             "/ assume behavior
  1815             ^ Behavior
  1906             ^ { Behavior }
  1816         ].
  1907         ].
  1817     ].
  1908     ].
  1818     ^ nil
  1909     ^ nil
  1819 
       
  1820     "Created: / 28-08-2013 / 16:34:53 / cg"
       
  1821 !
  1910 !
  1822 
  1911 
  1823 codeCompletionForLiteralSymbol:nodeOrNil element:tokenOrNil considerAll:considerAll into:actionBlock
  1912 codeCompletionForLiteralSymbol:nodeOrNil element:tokenOrNil considerAll:considerAll into:actionBlock
  1824     "looking for all symbols is way too much and inprecise;
  1913     "looking for all symbols is way too much and inprecise;
  1825      experiment: only present symbols which are used by the class,
  1914      experiment: only present symbols which are used by the class,
  1956 
  2045 
  1957     "/ Transcript show:'msg in '; show:methodOrNil; show:' / '; showCR:classOrNil.
  2046     "/ Transcript show:'msg in '; show:methodOrNil; show:' / '; showCR:classOrNil.
  1958 
  2047 
  1959     findBest := 
  2048     findBest := 
  1960         [:node :selector |
  2049         [:node :selector |
  1961             |srchClass srchClasses bestSelectors bestPrefixes
  2050             |srchClasses bestSelectors bestPrefixes
  1962              allMessagesSentToVariable classesImplementingAllMessages|
  2051              allMessagesSentToVariable classesImplementingAllMessages|
  1963 
  2052 
  1964             srchClass := self classOfNode:node.
  2053             srchClasses := self classesOfNode:node.
  1965 
  2054 
  1966             srchClass isNil ifTrue:[
  2055             srchClasses isEmptyOrNil ifTrue:[
  1967                 node isVariable ifTrue:[
  2056                 node isVariable ifTrue:[
  1968                     allMessagesSentToVariable := Set new.
  2057                     allMessagesSentToVariable := Set new.
  1969                     rememberedNodes do:[:eachNode |
  2058                     rememberedNodes do:[:eachNode |
  1970                         eachNode allMessageNodesDo:[:eachMessage |
  2059                         eachNode allMessageNodesDo:[:eachMessage |
  1971                             |msgReceiver msgSelector|
  2060                             |msgReceiver msgSelector|
  1989                         srchClasses := classesImplementingAllMessages.
  2078                         srchClasses := classesImplementingAllMessages.
  1990                     ].
  2079                     ].
  1991                 ].
  2080                 ].
  1992             ].
  2081             ].
  1993             bestSelectors := Set new.
  2082             bestSelectors := Set new.
  1994             srchClasses isEmptyOrNil ifTrue:[ srchClasses := Array with:srchClass ].
  2083             srchClasses isEmptyOrNil ifTrue:[
  1995             srchClasses do:[:srchClass |
  2084                 bestSelectors addAll:( Parser findBest:50 selectorsFor:selector in:nil forCompletion:true ).
  1996                 |bestForThisClass|
  2085             ] ifFalse:[    
  1997 
  2086                 srchClasses do:[:srchClass |
  1998                 bestForThisClass := Parser findBest:50 selectorsFor:selector in:srchClass forCompletion:true.
  2087                     |bestForThisClass|
  1999                 bestForThisClass := self
  2088 
  2000                                     withoutSelectorsUnlikelyFor:srchClass
  2089                     bestForThisClass := Parser findBest:50 selectorsFor:selector in:srchClass forCompletion:true.
  2001                                     from:bestForThisClass
  2090                     bestForThisClass := self
  2002                                     forPartial:selector.
  2091                                         withoutSelectorsUnlikelyFor:srchClass
  2003                 bestSelectors addAll:bestForThisClass.
  2092                                         from:bestForThisClass
       
  2093                                         forPartial:selector.
       
  2094                     bestSelectors addAll:bestForThisClass.
       
  2095                 ].
  2004             ].
  2096             ].
  2005             (bestSelectors includes:selector) ifTrue:[
  2097             (bestSelectors includes:selector) ifTrue:[
  2006                 bestSelectors := bestSelectors select:[:sel | sel size > selector size].
  2098                 bestSelectors := bestSelectors select:[:sel | sel size > selector size].
  2007             ].
  2099             ].
  2008             bestSelectors := bestSelectors asOrderedCollection.
  2100             bestSelectors := bestSelectors asOrderedCollection.
  2266 
  2358 
  2267     allBest := (bestSelectors ? #()) , (bestSelectors2 ? #()).
  2359     allBest := (bestSelectors ? #()) , (bestSelectors2 ? #()).
  2268     allBest sort:
  2360     allBest sort:
  2269         [:a :b |
  2361         [:a :b |
  2270             |aBeforeB|
  2362             |aBeforeB|
  2271             aBeforeB := a < b.
  2363             
  2272             (a asLowercase startsWith:lcSelector) ifTrue:[
  2364             (a startsWith:selector) ifTrue:[
  2273                 (b asLowercase startsWith:lcSelector) ifFalse:[
  2365                 (b startsWith:selector) ifFalse:[
  2274                     aBeforeB := true
  2366                     aBeforeB := true
  2275                 ]
  2367                 ]
  2276             ] ifFalse:[    
  2368             ] ifFalse:[    
  2277                 (b asLowercase startsWith:lcSelector) ifTrue:[
  2369                 (b startsWith:selector) ifTrue:[
  2278                     aBeforeB := false
  2370                     aBeforeB := false
  2279                 ]
  2371                 ]
       
  2372             ].
       
  2373             aBeforeB isNil ifTrue:[
       
  2374                 aBeforeB := a asLowercase < b asLowercase.
       
  2375                 (a asLowercase startsWith:lcSelector) ifTrue:[
       
  2376                     (b asLowercase startsWith:lcSelector) ifFalse:[
       
  2377                         aBeforeB := true
       
  2378                     ]
       
  2379                 ] ifFalse:[    
       
  2380                     (b asLowercase startsWith:lcSelector) ifTrue:[
       
  2381                         aBeforeB := false
       
  2382                     ]
       
  2383                 ].
  2280             ].
  2384             ].
  2281             aBeforeB
  2385             aBeforeB
  2282         ].
  2386         ].
  2283                         
  2387                         
  2284     split :=
  2388     split :=
  3849     characterBeforeCursor := source at:(characterPositionOfCursor-1 max:1). "/ codeView characterBeforeCursor.
  3953     characterBeforeCursor := source at:(characterPositionOfCursor-1 max:1). "/ codeView characterBeforeCursor.
  3850     characterBeforeCursor isNil ifTrue:[ "at begin of line" ^ self].
  3954     characterBeforeCursor isNil ifTrue:[ "at begin of line" ^ self].
  3851     characterBeforeCursor == $. ifTrue:[ "at end of statement" ^ self].
  3955     characterBeforeCursor == $. ifTrue:[ "at end of statement" ^ self].
  3852 
  3956 
  3853     node isVariable ifTrue:[
  3957     node isVariable ifTrue:[
       
  3958         |classes cls|
       
  3959         
  3854         nodeIsInTemporaries :=
  3960         nodeIsInTemporaries :=
  3855             nodeParent notNil
  3961             nodeParent notNil
  3856             and:[ nodeParent isSequence
  3962             and:[ nodeParent isSequence
  3857             and:[ nodeParent temporaries notEmptyOrNil
  3963             and:[ nodeParent temporaries notEmptyOrNil
  3858             and:[ node stop <= nodeParent temporaries last stop ]]].
  3964             and:[ node stop <= nodeParent temporaries last stop ]]].
       
  3965 
  3859         nodeIsInTemporaries ifFalse:[
  3966         nodeIsInTemporaries ifFalse:[
  3860             "/ cursor must be right after the variable
  3967             "/ cursor must be right after the variable
  3861             characterPositionOfCursor >= (node stop) ifTrue:[
  3968             codeView characterPositionOfCursor = (node stop + 1) ifTrue:[
  3862                 self codeCompletionForVariable:node into:actionBlock.
  3969                 self codeCompletionForVariable:node into:actionBlock.
       
  3970                 ^ self.
  3863             ]
  3971             ]
  3864         ].
  3972         ].
  3865         ^ self.
  3973 false ifTrue:[
       
  3974         codeView characterPositionOfCursor = (node stop + 2) ifTrue:[
       
  3975             "/ after a variable;
       
  3976             "/ offer local messages, if receiver type is known
       
  3977             classes := (self classesOfNode:node).
       
  3978             classes notEmptyOrNil ifTrue:[
       
  3979                 classes size > 1 ifTrue:[
       
  3980                     cls := classes anElement.
       
  3981                 ] ifFalse:[    
       
  3982                     cls := Behavior commonSuperclassOf:classes.
       
  3983                 ]
       
  3984             ]. 
       
  3985             cls notNil ifTrue:[
       
  3986                 |clsSelectors moreSelectors|
       
  3987             
       
  3988                 "/ completion in a message-send
       
  3989                 clsSelectors := cls methodDictionary keys. "/ Parser findBest:50 selectorsFor:'' in:cls forCompletion:true.
       
  3990                 clsSelectors size < 30 ifTrue:[
       
  3991                     cls superclass notNil ifTrue:[
       
  3992                         moreSelectors := cls superclass methodDictionary keys.
       
  3993                         clsSelectors size + moreSelectors size < 30 ifTrue:[
       
  3994                             clsSelectors := clsSelectors , moreSelectors.
       
  3995                         ].
       
  3996                     ].    
       
  3997                 ].
       
  3998                 "/ self codeCompletionForMessage:checkedNode into:actionBlock.
       
  3999                 actionBlock value:clsSelectors value:nil value:nil.
       
  4000                 ^ self.
       
  4001             ]
       
  4002         ]
       
  4003 ].
       
  4004 ^ self
  3866     ].
  4005     ].
  3867 
  4006 
  3868     node isLiteral ifTrue:[
  4007     node isLiteral ifTrue:[
  3869         "/ however, user may want to complete a symbol inside a literal array!!
  4008         "/ however, user may want to complete a symbol inside a literal array!!
  3870         node value isArray ifTrue:[
  4009         node value isArray ifTrue:[
  4255             ]
  4394             ]
  4256         ]    
  4395         ]    
  4257     ].
  4396     ].
  4258 
  4397 
  4259     findBest := [:node :selector |
  4398     findBest := [:node :selector |
  4260         |srchClass bestSelectors bestPrefixes|
  4399         |srchClasses bestSelectors bestPrefixes|
  4261 
  4400 
  4262         codeView withCursor:(Cursor questionMark) do:[
  4401         codeView withCursor:(Cursor questionMark) do:[
  4263             srchClass := self classOfNode:node receiver.
  4402             srchClasses := self classesOfNode:node receiver.
  4264             srchClass notNil ifTrue:[
  4403             srchClasses notNil ifTrue:[
  4265                 bestSelectors := Parser findBest:30 selectorsFor:selector in:srchClass forCompletion:true.
  4404                 bestSelectors := Set new.
       
  4405                 srchClasses do:[:each |
       
  4406                     bestSelectors addAll:(Parser findBest:30 selectorsFor:selector in:each forCompletion:true).
       
  4407                 ]    
  4266             ] ifFalse:[
  4408             ] ifFalse:[
  4267                 bestSelectors := Parser findBest:30 selectorsFor:selector in:nil forCompletion:true.
  4409                 bestSelectors := Parser findBest:30 selectorsFor:selector in:nil forCompletion:true.
  4268             ].
  4410             ].
  4269         ].
  4411         ].
  4270 
  4412