@ -1,11 +1,12 @@
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Reflection ;
using System.Text ;
using MediaBrowser.Model.Logging ;
using ServiceStack.Serialization ;
namespace ServiceStack
namespace Emby.Server.Implementations.Services
{
public class RestPath
{
@ -71,7 +72,7 @@ namespace ServiceStack
public static string [ ] GetPathPartsForMatching ( string pathInfo )
{
var parts = pathInfo . ToLower ( ) . Split ( PathSeperatorChar )
. Where ( x = > ! s tring. IsNullOrEmpty ( x ) ) . ToArray ( ) ;
. Where ( x = > ! S tring. IsNullOrEmpty ( x ) ) . ToArray ( ) ;
return parts ;
}
@ -107,14 +108,14 @@ namespace ServiceStack
return list ;
}
public RestPath ( Type requestType , string path , string verbs , string summary = null , string notes = null )
public RestPath ( Func< Type , object > createInstanceFn , Func < Type , Func < string , object > > getParseFn , Type requestType , string path , string verbs , string summary = null , string notes = null )
{
this . RequestType = requestType ;
this . Summary = summary ;
this . Notes = notes ;
this . restPath = path ;
this . allowsAllVerbs = verbs = = null | | s tring. Equals ( verbs , WildCard , StringComparison . OrdinalIgnoreCase ) ;
this . allowsAllVerbs = verbs = = null | | S tring. Equals ( verbs , WildCard , StringComparison . OrdinalIgnoreCase ) ;
if ( ! this . allowsAllVerbs )
{
this . allowedVerbs = verbs . ToUpper ( ) ;
@ -126,7 +127,7 @@ namespace ServiceStack
var hasSeparators = new List < bool > ( ) ;
foreach ( var component in this . restPath . Split ( PathSeperatorChar ) )
{
if ( s tring. IsNullOrEmpty ( component ) ) continue ;
if ( S tring. IsNullOrEmpty ( component ) ) continue ;
if ( StringContains ( component , VariablePrefix )
& & component . IndexOf ( ComponentSeperator ) ! = - 1 )
@ -199,19 +200,95 @@ namespace ServiceStack
this . IsValid = sbHashKey . Length > 0 ;
this . UniqueMatchHashKey = sbHashKey . ToString ( ) ;
this . typeDeserializer = new StringMapTypeDeserializer ( this . RequestType ) ;
this . typeDeserializer = new StringMapTypeDeserializer ( createInstanceFn , getParseFn , this . RequestType ) ;
RegisterCaseInsenstivePropertyNameMappings ( ) ;
}
private void RegisterCaseInsenstivePropertyNameMappings ( )
{
foreach ( var propertyInfo in RequestType. GetSerializableProperties( ) )
foreach ( var propertyInfo in GetSerializableProperties( RequestType ) )
{
var propertyName = propertyInfo . Name ;
propertyNamesMap . Add ( propertyName . ToLower ( ) , propertyName ) ;
}
}
internal static string [ ] IgnoreAttributesNamed = new [ ] {
"IgnoreDataMemberAttribute" ,
"JsonIgnoreAttribute"
} ;
private static List < Type > _excludeTypes = new List < Type > { typeof ( Stream ) } ;
internal static PropertyInfo [ ] GetSerializableProperties ( Type type )
{
var properties = GetPublicProperties ( type ) ;
var readableProperties = properties . Where ( x = > x . GetMethod ! = null ) ;
// else return those properties that are not decorated with IgnoreDataMember
return readableProperties
. Where ( prop = > prop . GetCustomAttributes ( true )
. All ( attr = >
{
var name = attr . GetType ( ) . Name ;
return ! IgnoreAttributesNamed . Contains ( name ) ;
} ) )
. Where ( prop = > ! _excludeTypes . Contains ( prop . PropertyType ) )
. ToArray ( ) ;
}
private static PropertyInfo [ ] GetPublicProperties ( Type type )
{
if ( type . GetTypeInfo ( ) . IsInterface )
{
var propertyInfos = new List < PropertyInfo > ( ) ;
var considered = new List < Type > ( ) ;
var queue = new Queue < Type > ( ) ;
considered . Add ( type ) ;
queue . Enqueue ( type ) ;
while ( queue . Count > 0 )
{
var subType = queue . Dequeue ( ) ;
foreach ( var subInterface in subType . GetTypeInfo ( ) . ImplementedInterfaces )
{
if ( considered . Contains ( subInterface ) ) continue ;
considered . Add ( subInterface ) ;
queue . Enqueue ( subInterface ) ;
}
var typeProperties = GetTypesPublicProperties ( subType ) ;
var newPropertyInfos = typeProperties
. Where ( x = > ! propertyInfos . Contains ( x ) ) ;
propertyInfos . InsertRange ( 0 , newPropertyInfos ) ;
}
return propertyInfos . ToArray ( ) ;
}
return GetTypesPublicProperties ( type )
. Where ( t = > t . GetIndexParameters ( ) . Length = = 0 ) // ignore indexed properties
. ToArray ( ) ;
}
private static PropertyInfo [ ] GetTypesPublicProperties ( Type subType )
{
var pis = new List < PropertyInfo > ( ) ;
foreach ( var pi in subType . GetRuntimeProperties ( ) )
{
var mi = pi . GetMethod ? ? pi . SetMethod ;
if ( mi ! = null & & mi . IsStatic ) continue ;
pis . Add ( pi ) ;
}
return pis . ToArray ( ) ;
}
public bool IsValid { get ; set ; }
/// <summary>
@ -243,7 +320,7 @@ namespace ServiceStack
score + = Math . Max ( ( 10 - VariableArgsCount ) , 1 ) * 100 ;
//Exact verb match is better than ANY
var exactVerb = s tring. Equals ( httpMethod , AllowedVerbs , StringComparison . OrdinalIgnoreCase ) ;
var exactVerb = S tring. Equals ( httpMethod , AllowedVerbs , StringComparison . OrdinalIgnoreCase ) ;
score + = exactVerb ? 10 : 1 ;
return score ;
@ -339,7 +416,7 @@ namespace ServiceStack
private bool LiteralsEqual ( string str1 , string str2 )
{
// Most cases
if ( s tring. Equals ( str1 , str2 , StringComparison . OrdinalIgnoreCase ) )
if ( S tring. Equals ( str1 , str2 , StringComparison . OrdinalIgnoreCase ) )
{
return true ;
}
@ -349,7 +426,7 @@ namespace ServiceStack
str2 = str2 . ToUpperInvariant ( ) ;
// Invariant IgnoreCase would probably be better but it's not available in PCL
return s tring. Equals ( str1 , str2 , StringComparison . CurrentCultureIgnoreCase ) ;
return S tring. Equals ( str1 , str2 , StringComparison . CurrentCultureIgnoreCase ) ;
}
private bool ExplodeComponents ( ref string [ ] withPathInfoParts )
@ -358,7 +435,7 @@ namespace ServiceStack
for ( var i = 0 ; i < withPathInfoParts . Length ; i + + )
{
var component = withPathInfoParts [ i ] ;
if ( s tring. IsNullOrEmpty ( component ) ) continue ;
if ( S tring. IsNullOrEmpty ( component ) ) continue ;
if ( this . PathComponentsCount ! = this . TotalComponentsCount
& & this . componentsWithSeparators [ i ] )
@ -380,7 +457,7 @@ namespace ServiceStack
public object CreateRequest ( string pathInfo , Dictionary < string , string > queryStringAndFormData , object fromInstance )
{
var requestComponents = pathInfo . Split ( PathSeperatorChar )
. Where ( x = > ! s tring. IsNullOrEmpty ( x ) ) . ToArray ( ) ;
. Where ( x = > ! S tring. IsNullOrEmpty ( x ) ) . ToArray ( ) ;
ExplodeComponents ( ref requestComponents ) ;
@ -390,7 +467,7 @@ namespace ServiceStack
& & requestComponents . Length > = this . TotalComponentsCount - this . wildcardCount ;
if ( ! isValidWildCardPath )
throw new ArgumentException ( s tring. Format (
throw new ArgumentException ( S tring. Format (
"Path Mismatch: Request Path '{0}' has invalid number of components compared to: '{1}'" ,
pathInfo , this . restPath ) ) ;
}
@ -409,14 +486,14 @@ namespace ServiceStack
string propertyNameOnRequest ;
if ( ! this . propertyNamesMap . TryGetValue ( variableName . ToLower ( ) , out propertyNameOnRequest ) )
{
if ( s tring. Equals ( "ignore" , variableName , StringComparison . OrdinalIgnoreCase ) )
if ( S tring. Equals ( "ignore" , variableName , StringComparison . OrdinalIgnoreCase ) )
{
pathIx + + ;
continue ;
}
throw new ArgumentException ( "Could not find property "
+ variableName + " on " + RequestType . Get Operation Name( ) ) ;
+ variableName + " on " + RequestType . Get Method Name( ) ) ;
}
var value = requestComponents . Length > pathIx ? requestComponents [ pathIx ] : null ; //wildcard has arg mismatch
@ -439,12 +516,12 @@ namespace ServiceStack
// hits a match for the next element in the definition (which must be a literal)
// It may consume 0 or more path parts
var stopLiteral = i = = this . TotalComponentsCount - 1 ? null : this . literalsToMatch [ i + 1 ] ;
if ( ! s tring. Equals ( requestComponents [ pathIx ] , stopLiteral , StringComparison . OrdinalIgnoreCase ) )
if ( ! S tring. Equals ( requestComponents [ pathIx ] , stopLiteral , StringComparison . OrdinalIgnoreCase ) )
{
var sb = new StringBuilder ( ) ;
sb . Append ( value ) ;
pathIx + + ;
while ( ! s tring. Equals ( requestComponents [ pathIx ] , stopLiteral , StringComparison . OrdinalIgnoreCase ) )
while ( ! S tring. Equals ( requestComponents [ pathIx ] , stopLiteral , StringComparison . OrdinalIgnoreCase ) )
{
sb . Append ( PathSeperatorChar + requestComponents [ pathIx + + ] ) ;
}