diff --git a/pmd-objectivec/etc/grammar/ObjC2.0.jj b/pmd-objectivec/etc/grammar/ObjC2.0.jj new file mode 100644 index 0000000000..63dca65fa1 --- /dev/null +++ b/pmd-objectivec/etc/grammar/ObjC2.0.jj @@ -0,0 +1,1407 @@ +/** + * This Objective-C grammar was copied and adapted from the Objective-C-CPD-Language Github project. (https://github.com/jkennedy1980/Objective-C-CPD-Language) + * + * Original author: + * @author Michael Hall - Based on Parnel SableCC ObjectiveC grammar and javacc C grammar + * + * Original source file: + * https://github.com/jkennedy1980/Objective-C-CPD-Language/blob/master/src/main/javacc/ObjC2.0.jj (commit 5b07b5cc424522e435929bbff7589c690100edf6) + */ + +options { + JAVA_UNICODE_ESCAPE = true; + BUILD_PARSER=false; + CACHE_TOKENS=true; + STATIC=false; +} + +PARSER_BEGIN(ObjectiveCParser) +package net.sourceforge.pmd.lang.objectivec.ast; + +import java.io.*; +import java.util.*; + +import net.sourceforge.pmd.lang.ast.CharStream; +import net.sourceforge.pmd.lang.ast.TokenMgrError; + +/** + * Grammar to parse ObjectiveC 2.0 + * @author Michael Hall - Based on Parnel SableCC ObjectiveC grammar and javacc C grammar + * + * Josh Kennedy - added CSTRING_LITERAL so parser doesn't die on CStrings. + * Paul Taykalo - added support for 0x literals, @compatibility_alias, and the $ character identifier + * Paul Taykalo - vertical tab (\v) and alert (\a) characters are added + */ + +public class ObjectiveCParser +{ + + // Simple logging flag + private static final boolean verbose = false; + + // HashSet for omitted test cases + private static final Set omissions = new HashSet(); + + static { + /* Foundation */ + omissions.add("Foundation.h"); + /* AppKit */ + omissions.add("AppKit.h"); /* All #import's */ + omissions.add("AppKitDefines.h"); /* All preprocessor */ + omissions.add("NSSpellServer.h"); + omissions.add("NSNibDeclarations.h"); + /* CoreData */ + omissions.add("CoreData.h"); /* All #import's */ + /* CoreFoundation */ + omissions.add("CoreFoundation.h"); /* All #import's */ + /* CoreLocation */ + omissions.add("CoreLocation.h"); /* All #import's */ + /* Quartz Core */ + omissions.add("CoreAnimation.h"); /* All #import's */ + omissions.add("CoreImage.h"); /* All #import's */ + omissions.add("CoreVideo.h"); /* All #import's */ + omissions.add("CVBase.h"); /* All #import's */ + omissions.add("CVBuffer.h"); /* All #import's */ + omissions.add("CVDisplayLink.h"); /* All #import's */ + omissions.add("CVHostTime.h"); /* All #import's */ + omissions.add("CVImageBuffer.h"); /* All #import's */ + omissions.add("CVOpenGLBuffer.h"); /* All #import's */ + omissions.add("CVOpenGLBufferPool.h"); /* All #import's */ + omissions.add("CVOpenGLTexture.h"); /* All #import's */ + omissions.add("CVOpenGLTextureCache.h"); /* All #import's */ + omissions.add("CVPixelBuffer.h"); /* All #import's */ + omissions.add("CVPixelBufferPool.h"); /* All #import's */ + omissions.add("CVPixelFormatDescription.h"); /* All #import's */ + omissions.add("CVReturn.h"); /* All #import's */ + omissions.add("QuartzCore.h"); /* All #import's */ + /* WebKit */ + omissions.add("DOM.h"); /* All #import's */ + omissions.add("DOMCore.h"); /* All #import's */ + omissions.add("DOMEvents.h"); /* All #import's */ + omissions.add("DOMHTML.h"); /* All #import's */ + omissions.add("DOMRanges.h"); /* All #import's */ + omissions.add("DOMStylesheets.h"); /* All #import's */ + omissions.add("DOMTraversal.h"); /* All #import's */ + omissions.add("DOMViews.h"); /* All #import's */ + omissions.add("DOMXPath.h"); /* All #import's */ + omissions.add("WebKit.h"); /* All #import's */ + /* Preference Panes */ + omissions.add("PreferencePanes.h"); /* All #import's */ + } + + // HashSet for storing typedef types + private static Set types = new HashSet(); + + // Stack for determining when the parser + // is parsing a typedef definition. + private static Stack typedefParsingStack = new Stack(); + + // Returns true if the given string is + // a typedef type. + private static boolean isType(String type){ + return types.contains(type); + } + + // Returns true if the given string is + // in the test case omission list + private static boolean isOmitted(String fileName) { + return omissions.contains(fileName); + } + + // Add a typedef type to those already defined + private static void addType(String type){ + types.add(type); + } + + // Prints out all the types used in parsing the c source + private static void printTypes(){ + for (Iterator i = types.iterator(); i.hasNext();) { + System.out.println(i.next()); + } + } + + public ObjectiveCParser(String fileName) + { + this(System.in); + try { ReInit(new FileInputStream(new File(fileName))); } + catch(Exception e) { e.printStackTrace(); } + } + + public static void main(String args[]) { + ObjectiveCParser parser = null; + String ps = System.getProperty("path.separator"); + + // Hack to include type "special types" + types.add("__signed__"); + types.add("__const"); + types.add("__inline__"); + types.add("__signed"); + + if (args.length == 0) { + System.out.println("ObjectiveC 2.0 Parser Version 1.0: Reading from standard input . . ."); + parser = new ObjectiveCParser(System.in); + } else if (args.length == 1) { + if (new File(args[0]).isDirectory()) { + String[] files = new File(args[0]).list(); + for (int i=0;i /* mjh - not currently of interest */ +| "NS_FORMAT_FUNCTION" : FORMAT_FUNC +| "CF_FORMAT_ARGUMENT" : FORMAT_FUNC +| "__OSX_AVAILABLE_STARTING" : FORMAT_FUNC /* mjh - same sort of thing */ +| "WEBKIT_OBJC_METHOD_ANNOTATION" : FORMAT_FUNC /* mjh - same sort of thing */ +| "(void (^)" : VOID_BLOCK +} + +/* mjh - we try to just ignore the parameter list */ + SKIP : +{ + "))" : DEFAULT +} + + MORE: +{ + < ~[")"] > +} + + SKIP: +{ + "\n" : DEFAULT +} + + MORE: +{ + "\\\n" + | + "\\\r\n" + | + < ~[] > +} + +/* mjh - we try to just ignore the "(" whatever ")" */ + SKIP : +{ + ")" : DEFAULT +} + + SPECIAL_TOKEN : +{ + +} + + MORE : +{ + "\\\n" + | + "\\\r\n" + | + < ~[")"] > +} + +/* COMMENTS */ + +MORE : +{ + "/*" : IN_MULTI_LINE_COMMENT +} + +SPECIAL_TOKEN : +{ + +} + + +SPECIAL_TOKEN : +{ + : DEFAULT +} + + +MORE : +{ + < ~[] > +} + +/* OPERATORS */ + +TOKEN : +{ + < ASSIGN: "=" > +| < LT: "<" > +| < GT: ">" > +| < BANG: "!" > +| < TILDE: "~" > +| < HOOK: "?" > +| < COLON: ":" > +| < EQ: "==" > +| < LE: "<=" > +| < GE: ">=" > +| < NE: "!=" > +| < SC_OR: "||" > +| < SC_AND: "&&" > +| < INCR: "++" > +| < DECR: "--" > +| < PLUS: "+" > +| < MINUS: "-" > +| < STAR: "*" > +| < SLASH: "/" > +| < BIT_AND: "&" > +| < BIT_OR: "|" > +| < XOR: "^" > +| < REM: "%" > +| < LSHIFT: "<<" > +| < PLUSASSIGN: "+=" > +| < MINUSASSIGN: "-=" > +| < STARASSIGN: "*=" > +| < SLASHASSIGN: "/=" > +| < ANDASSIGN: "&=" > +| < ORASSIGN: "|=" > +| < XORASSIGN: "^=" > +| < REMASSIGN: "%=" > +| < LSHIFTASSIGN: "<<=" > +| < RSIGNEDSHIFTASSIGN: ">>=" > +| < RUNSIGNEDSHIFTASSIGN: ">>>=" > +| < ELLIPSIS: "..." > +} + +TOKEN : +{ + < ESCAPE_SEQUENCE : + "\\" ["'","\"","?","\\","a","b","f","n","r","t","v"] /* SIMPLE_ESCAPE_SEQUENCE */ + | "\\" ( ()?)? /* OCTAL_ESCAPE_SEQUENCE */ + | "\\" "x" ()+ /* HEX_ESCAPE_SEQUENCE */ + > +} + +TOKEN : { + (["l","L"] | ("ul" | "UL") )? | (["l","L","U"])? | (["l","L"])?> +| <#DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* | (["0"])+> +| <#HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+> +| <#OCTAL_LITERAL: "0" (["0"-"7"])*> +| )? (["f","F","d","D"])? | "." (["0"-"9"])+ ()? (["f","F","d","D"])? | (["0"-"9"])+ (["f","F","d","D"])? | (["0"-"9"])+ ()? ["f","F","d","D"]> +| <#EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+> +| +| +| +} + +TOKEN : +{ + < DIGIT: ["0"-"9"] > +| + < NONZERO_DIGIT : ["1"-"9"] > +| + < OCTAL_DIGIT : ["1"-"7"] > +| + < #HEX_DIGIT : | ["a"-"f"] | ["A"-"F"] > +| + < #HEX_QUAD : > +| + < #UNSIGNED_SUFFIX : ["u","U"] | ("UL" | "ul") > +| + < #LONG_SUFFIX : ["l","L"] | ("LL" | "ll") > +| + < #INTEGER_SUFFIX : ()? | ()? > +| + < DECIMAL_CONSTANT : ()* > +| + < OCTAL_CONSTANT : "0" | > +| + < HEX_PREFIX : "0" ["x","X"] > +| + < HEX_CONSTANT : ()+ > +} + +TOKEN : +{ + < INTEGER_CONSTANT : + ()? + | ()? + | ()? + > +} + +TOKEN : { + +} + +TOKEN : { + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + +} + +TOKEN : { + + | + | + | + | + | + | +} + +/* mjh - see ProtocolQualifier +TOKEN : +{ + +} +*/ + +TOKEN : +{ + +} + +TOKEN : +{ + + | + | + | + | + | + | +} + +TOKEN : +{ + + | + | + | + | + | + | + | + | + | + + | + | + | + | + + | + | + | + | + + | + | + | + + | + | + | + | + +} + +TOKEN : +{ + + | + | + | + | +} + +TOKEN : { + | "_")*> + | | "_")*> +} + +TOKEN : { } + +TOKEN : +{ + < IDENT : ( | )* > +| <#IDENT_NONDIGIT : | > // "may include other implementation-defined characters" +| <#NONDIGIT : ["a"-"z"] | ["A"-"Z"] | "_" | "$" > +| <#UNIVERSAL_CHARACTER_NAME : ("\\u" ) | ("\\U" ) > +} + +TOKEN : { } + +/*********************************************** + * THE OBJECTIVEC LANGUAGE GRAMMAR STARTS HERE * + ***********************************************/ + +void TranslationUnit() : {} +{ + (ExternalDeclaration())+ +} + + + +void ExternalDeclaration() : {} +{ + ( LOOKAHEAD( FunctionDefinition() ) FunctionDefinition() + | LOOKAHEAD(3) StrippedParens() /* mjh if we skip all but parens for something like __attribute__((__objc_exception__)) */ + | Declaration() + | LOOKAHEAD(3) ClassInterface() + | LOOKAHEAD(3) ClassImplementation() + | CategoryInterface() + | CategoryImplementation() + | ProtocolDeclaration() + | ClassDeclarationList() ) +} + +void StrippedParens() : {} /* See ExternalDeclaration above */ +{ + [ StrippedParens() ] +} + +void FunctionDefinition() : {} +{ + [LOOKAHEAD(DeclarationSpecifiers()) DeclarationSpecifiers()] Declarator() [ DeclarationList() ] + CompoundStatement() +} + +void Declaration() : {} +{ + DeclarationSpecifiers() [ InitDeclaratorList() ] [ "(" ParameterTypeList() ")" "(" ParameterTypeList() ")" ] /* ugly hack for single header typedef */ ";" +} + +void ClassInterface() : {} +{ + ClassName() (ColonSuperClassName())? (ProtocolReferenceList())? + [ LOOKAHEAD(2) ["{"] ClassName() (ColonSuperClassName())? (ProtocolReferenceList())? ] /* dup for preprocessor #else */ + (InstanceVariables())? (InterfaceDeclaration())* +} + +void ClassImplementation() : {} +{ + ClassName() (LOOKAHEAD(2) ColonSuperClassName())? (InstanceVariables())? (ImplementationDefinition())* +} + +void CategoryInterface() : {} +{ + ClassName() (CategoryName())? (ProtocolReferenceList())? (InterfaceDeclaration())* +} + +void CategoryImplementation() : {} +{ + ClassName() CategoryName() (ImplementationDefinition())* +} + +void ProtocolDeclaration() : {} +{ + ProtocolList() (ProtocolReferenceList())? ProtocolInterfaceDeclaration() ( | ";" ) +} + +void ClassDeclarationList() : {} +{ + ClassList() ";" +} + +void ClassList() : {} +{ + ClassName() ["," ClassList()] +} + +void ProtocolReferenceList() : {} +{ + ProtocolList() [ ProtocolList() /* if preproc #else, see AppKit NSWindow */ ] +} + +void ProtocolList() : {} +{ + ProtocolName() ["," ProtocolList()] +} + +Token ObjCIDENT() : { Token t; } +{ + t = { return t; } | t = { return t; } | t = { return t; } +} + +void ClassName() : {} +{ + ObjCIDENT() +} + +void SuperClassName() : {} +{ + ObjCIDENT() +} + +void ColonSuperClassName() : {} +{ + SuperClassName() +} + +void CategoryName() : {} +{ + | | +} + +void ProtocolName() : {} +{ + | | +} + +void InstanceVariables() : {} +{ + (InstanceVariableDeclaration())* [ ";" ] +} + +void InstanceVariableDeclaration() : {} +{ + VisibilitySpecification() + | LOOKAHEAD(3) InstanceVariableDeclarator() + | StructDeclaration() +} + +void InstanceVariableDeclarator() : {} +{ + ( LOOKAHEAD(3) [ IBOutlet() ] + (LOOKAHEAD(2) ClassName() "*" | [ ProtocolReferenceList() ]) ";" + | Declaration() ) +} + +void VisibilitySpecification() : {} +{ + [ ";" ] + | + | + | +} + +void IBOutlet() : {} /* mjh */ +{ + +} + +void ProtocolInterfaceDeclaration() : {} +{ + (InterfaceDeclaration())* (QualifiedProtocolInterfaceDeclaration())* +} + +void QualifiedProtocolInterfaceDeclaration() : {} +{ + (InterfaceDeclaration())* + | (InterfaceDeclaration())* +} + +void InterfaceDeclaration() : {} +{ + Declaration() + | PropertyDeclaration() + | MethodDeclaration() +} + +void PropertyDeclaration() : {} +{ + (PropertyAttributesDeclaration())? ( LOOKAHEAD(3) StructDeclaration() | (ProtocolReferenceList())? ";" ) +} + +void PropertyAttributesDeclaration() : {} +{ + PropertyAttributesList() +} + +void PropertyAttributesList() : {} +{ + PropertyAttribute() ["," PropertyAttributesList()] +} + +void PropertyAttribute() : {} +{ + LOOKAHEAD(3) /* prop */ [ ] // setter +// | LOOKAHEAD(2) /* getter or ivar */ // getter ivar + | // nonatomic, readwrite, readonly, retain, assign, copy + | /* NS_NONATOMIC_IPHONEONLY */ +} + +void MethodDeclaration() : {} +{ + ClassMethodDeclaration() + | InstanceMethodDeclaration() +} + +void ClassMethodDeclaration() : {} +{ + (MethodType())? MethodSelector() +} + +void InstanceMethodDeclaration() : {} +{ + (MethodType())? MethodSelector() +} + +void ImplementationDefinition() : {} +{ + ( LOOKAHEAD( FunctionDefinition() ) FunctionDefinition() + | Declaration() + | PropertyImplementation() + | MethodDefinition() ) +} + +void PropertyImplementation() : {} +{ + PropertySynthesizeList() + | PropertySynthesizeList() +} + +void PropertySynthesizeList() : {} +{ + PropertySynthesizeItem() ["," PropertySynthesizeList()] +/* + PropertySynthesizeList() "," PropertySynthesizeItem() + | PropertySynthesizeItem() +*/ +} + +void PropertySynthesizeItem() : {} +{ + LOOKAHEAD(2) + | +} + +void MethodDefinition() : {} +{ + ClassMethodDefinition() + | InstanceMethodDefinition() +} + +void ClassMethodDefinition() : {} +{ + (MethodType())? MethodSelectorNoList() (Declaration())* ()? CompoundStatement() +} + +void InstanceMethodDefinition() : {} +{ + (MethodType())? MethodSelectorNoList() (Declaration())* ()? CompoundStatement() +} + +void MethodSelectorNoList() : {} +{ + LOOKAHEAD(3) KeywordSelector() [LOOKAHEAD(2) (",..." | "," "...")] + | UnarySelector() +} + +void MethodSelector() : {} +{ + LOOKAHEAD(3) MethodSelectorNoList() + | KeywordSelector() "," ParameterTypeList() // this is correct according to objcbook, but causes conflict if followed by declaration* +} + +void UnarySelector() : {} +{ + Selector() +} + +void KeywordSelector() : {} +{ + (LOOKAHEAD(2) KeywordDeclarator())+ +} + +void KeywordDeclarator() : {} +{ + (Selector())? (MethodType())? +} + +void Selector() : {} +{ + +} + +void MethodType() : {} +{ + (LOOKAHEAD(3) Block() | TypeNameWithUnknownType()) +} + +void SelectorExpression() : {} +{ + SelectorName() +} + +void SelectorName() : {} +{ + LOOKAHEAD(2) Selector() + | (KeywordName())+ +} + +void KeywordName() : {} +{ + (Selector())? +} + +void ProtocolExpression() : {} +{ + ProtocolName() +} + +void EncodeExpression() : {} +{ + TypeNameWithUnknownType() +} + +void DeclarationList() : {} +{ + ( LOOKAHEAD(Declaration()) Declaration() )+ +} + +void DeclarationSpecifiers() : {} +{ + /** + * We enumerate the possibilities (closure?) like SpecifierQualifierList + * instead of the standard C grammar recursive definition + * Although more complex it might actually end up cleaner? + * (For one thing you don't keep recursively matching types + * in TypeSpecifier(), this can muddle things or require semantic lookahead + * workarounds, see the sample javacc C implementation of this nonterminal) + * + * Assumes none actually should be duplicated + */ + LOOKAHEAD(3) TypeSpecifier() [ StorageClassSpecifier() ] [ TypeQualifier() ] + | TypeSpecifier() [ TypeQualifier() ] [ StorageClassSpecifier() ] + | LOOKAHEAD(3) StorageClassSpecifier() [ TypeQualifier() ] [ LOOKAHEAD(2) TypeSpecifier() ] + | LOOKAHEAD(3) StorageClassSpecifier() [ LOOKAHEAD(2) TypeSpecifier() ] [ TypeQualifier() ] + | LOOKAHEAD(3) TypeQualifier() [ StorageClassSpecifier() ] [ LOOKAHEAD(2) TypeSpecifier() ] + | LOOKAHEAD(2) TypeQualifier() [ LOOKAHEAD(2) TypeSpecifier() ] [ StorageClassSpecifier() ] +} + +void StorageClassSpecifier() : {} +{ + ( | | | | + { + typedefParsingStack.push(Boolean.TRUE); + } ) +} + +void TypeSpecifier() : {} +{ + ( GreedyFixedNumType() | | | | | | + /* mjh - same as void */ | | /* Cocoa additions */ + | + | | | | | | | | /* mjh - more cocoa additions */ + | | /* CoreFoundation additions */ + | | StructOrUnionSpecifier() | EnumSpecifier() | LOOKAHEAD( { isType(getToken(1).image) } )TypedefName() | + PossibleCocoaType() | PossibleCoreType() | PossibleUnknownType() + ) +} + +void GreedyFixedNumType() : {} +{ + LOOKAHEAD(2) [ | ] [ LOOKAHEAD(2) ] /* long long */ | + | + +} + +void PossibleCocoaType() : { Token t; } +{ + t = + { + if (verbose) + System.out.println("WARNING: Adding typedef on possible Cocoa type " + t.image + " at line number " + t.beginLine + ", column number " + t.beginColumn); + addType(t.image); + } +} + +void PossibleCoreType() : { Token t; } +{ + t = + { + if (verbose) + System.out.println("WARNING: Adding typedef on possible CoreFoundation type " + t.image + " at line number " + t.beginLine + ", column number " + t.beginColumn); + addType(t.image); + } +} + +void PossibleUnknownType() : { Token t; } +{ + t = + { + if (verbose) + System.out.println("WARNING: Adding typedef on unknown identifier " + t.image + " at line number " + t.beginLine + ", column number " + t.beginColumn); + addType(t.image); + } +} + +void TypeSpecifierWithUnknownType() : {} +{ + LOOKAHEAD(2) (ObjCIDENT() | ) (ProtocolReferenceList())? + | TypeSpecifier() +} + +void TypeQualifier() : {} +{ + ( | ) +} + +void StructOrUnionSpecifier() : {} +{ + { + typedefParsingStack.push(Boolean.FALSE); + } + + StructOrUnion() ( LOOKAHEAD(3) [ LOOKAHEAD(2) ObjCIDENT() ] ( "{" StructDeclarationList() "}" | ObjCIDENT() ) | ) + + { + typedefParsingStack.pop(); + } +} + +void StructOrUnion() : {} +{ + ( | ) +} + +void StructDeclarationList() : {} +{ + (StructDeclaration())+ +} + +void InitDeclaratorList() : {} +{ + InitDeclarator() ("," InitDeclarator())* + { + // Finished with a typedefDeclaration?? + if(!(typedefParsingStack.empty()) && ((Boolean)typedefParsingStack.peek()).booleanValue()){ + typedefParsingStack.pop(); + } + } +} + +void InitDeclarator() : {} +{ + Declarator() [ "=" Initializer() ] +} + +void ProtocolQualifier() : {} +{ + + | /* */ In() /* Making it a IN_KEYWORD token disallows other uses */ +} + +void In() : {} +{ + LOOKAHEAD( { getToken(0).image.equals("in") } ) +} + +void StructDeclaration() : {} +{ + SpecifierQualifierList() ( StructDeclaratorList() | "[" ConstantExpression() "]" ) ";" +} + +void SpecifierQualifierWithUnknownType() : {} +{ + LOOKAHEAD(2) TypeSpecifierWithUnknownType() + | TypeQualifier() + | ProtocolQualifier() +} + +void SpecifierQualifierList() : {} +{ + /** + * Since we don't know what all possible typedef's might be and end up matching any as a possible type + * we could recursively match both tokens in something like... + * CFIndex version; + * to PossibleUnknownType() in TypeSpecifier(). + * So we don't follow the elegant C recursive definition but enumeratte the two possible combinations + */ + TypeSpecifier() [ TypeQualifier() ] + | TypeQualifier() TypeSpecifier() +} + +void StructDeclaratorList() : {} +{ + StructDeclarator() ( "," StructDeclarator() )* +} + +void StructDeclarator() : {} +{ + Declarator() +} + +void EnumSpecifier() : {} +{ + ( LOOKAHEAD(3) [ ObjCIDENT() ] "{" EnumeratorList() "}" | ) +} + +void EnumeratorList() : {} +{ + Enumerator() ("," [ Enumerator() ])* +} + +void Enumerator() : {} +{ + ObjCIDENT() [ "=" ConstantExpression() ] +} + +void Declarator() : {} +{ + LOOKAHEAD(3) Block() + | LOOKAHEAD(3) [ Pointer() ] DirectDeclarator() [ ":" ConstantExpression() ] + | ":" ConstantExpression() + | LOOKAHEAD(2) TypeSpecifier() // trying to handle 'typedef type-spec type-spec' special case, a little ugly +} + +void Block() : {} +{ + ( LOOKAHEAD(3) | | | ObjCIDENT() [ "*" ])? [ ObjCIDENT() ] ParameterList() +} + +void DirectDeclarator() : { Token t;} +{ + ( t = ObjCIDENT() + + { if(!(typedefParsingStack.empty()) && ((Boolean)typedefParsingStack.peek()).booleanValue()){ + addType(t.image); + } + } + | LOOKAHEAD(3) "(" Declarator() ")" ) + + { typedefParsingStack.push( Boolean.FALSE ); } + + ( "[" [ ConstantExpression() ] "]" | + LOOKAHEAD(3) "(" ParameterTypeList() ")" | + "(" [ IdentifierList() ] ")" )* + { typedefParsingStack.pop(); } +} + +void Pointer() : {} +{ + "*" [ TypeQualifierList() ] [ Pointer() ] +} + +void TypeQualifierList() : {} +{ + (TypeQualifier())+ +} + +void ParameterTypeList() : {} +{ + ParameterList() ["," "..." ] +} + +void ParameterList() : {} +{ + ParameterDeclaration() (LOOKAHEAD(2) "," ParameterDeclaration())* +} + +void ParameterDeclaration() : {} +{ + DeclarationSpecifiers() ( LOOKAHEAD(Declarator()) Declarator() | [ AbstractDeclarator() ] ) +} + +void IdentifierList() : {} +{ + ("," )* +} + +void Initializer() : {} +{ + ( AssignmentExpression() | + "{" InitializerList() [","] "}" ) +} + +void InitializerList() : {} +{ + Initializer() (LOOKAHEAD(2) "," Initializer())* +} + +void TypeName() : {} +{ + SpecifierQualifierList() [ AbstractDeclarator() ] +} + +void TypeNameWithUnknownType() : {} +{ + (SpecifierQualifierWithUnknownType())+ (AbstractDeclarator())? +} + +void AbstractDeclarator() : {} +{ + ( LOOKAHEAD(3) Pointer() | + [Pointer()] DirectAbstractDeclarator() ) +} + +void DirectAbstractDeclarator() : {} +{ + ( LOOKAHEAD(2) "(" AbstractDeclarator() ")" | + "[" [ConstantExpression()] "]" | + "(" [ParameterTypeList()] ")" ) + + ( "[" [ ConstantExpression() ] "]" | "(" [ ParameterTypeList() ] ")" )* +} + +void TypedefName() : {} +{ + ObjCIDENT() +} + +void Statement() : {} +{ + + ( LOOKAHEAD(2) LabeledStatement() | + LOOKAHEAD(3) InitStatement() | + ExpressionStatement() | + CompoundStatement() | + SelectionStatement() | + IterationStatement() | + JumpStatement() ) +} + +void LabeledStatement() : {} +{ + ( ":" Statement() | + ConstantExpression() ":" Statement() | + ":" Statement() ) +} + +void InitStatement() : {} /* mjh not part of normal C grammar but works */ +{ + [ LOOKAHEAD(2) TypeSpecifier() ] ["*"] ("," )* + ("=" ( LOOKAHEAD(3) MessageExpression() | PostfixExpression() ) ( ";" | "," InitStatement() ) + | ";") /* catch embedded variable declarations */ +} + +void ExpressionStatement() : {} +{ + [ Expression() ] ";" +} + +void CompoundStatement() : {} +{ + "{" ( LOOKAHEAD(DeclarationList()) DeclarationList() | StatementList() )* "}" +} + +void StatementList() : {} +{ + (LOOKAHEAD(2) Statement())+ +} + +void SelectionStatement() : {} +{ + ( "(" [ LOOKAHEAD(2) NestedLogicalExpression() ] Expression() ")" Statement() [ LOOKAHEAD(2) Statement() ] | + "(" Expression() ")" Statement() ) +} + +void NestedLogicalExpression() : {} /* mjh - introduced as part of nesting handling */ +{ + "(" [ LOOKAHEAD(2) NestedLogicalExpression() ] Expression() ")" LogicalOperator() +} + +void LogicalOperator() : {} /* mjh - introduced as part of nesting handling */ +{ + | | | +} + +void IterationStatement() : {} +{ + ( "(" Expression() ")" Statement() | + Statement() "(" Expression() ")" ";" | + "(" + ( LOOKAHEAD(3) TypeSpecifier() In() PrimaryExpression() | + [ LOOKAHEAD(NumberTypeInit()) NumberTypeInit() ] [ Expression() ] ";" [ Expression() ] ";" [ Expression() ] ) + ")" Statement() + ) +} + +void NumberTypeInit() : {} /* mjh - allow number type initializers, e.g. on 'for' statement inits */ +{ + ( | | ) +} + +void JumpStatement() : {} +{ + ( ";" | + ";" | + ";" | + [ Expression() ] ";" ) +} + +void Expression() : {} +{ + AssignmentExpression() ( "," AssignmentExpression() )* +} + +void AssignmentExpression() : {} +{ + LOOKAHEAD(UnaryExpression() AssignmentOperator()) UnaryExpression() AssignmentOperator() AssignmentExpression() | + LOOKAHEAD(3) ConditionalExpression() +} + +void AssignmentOperator() : {} +{ + ( "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" ) +} + +void ConditionalExpression() : {} +{ + LogicalORExpression() [ "?" Expression() ":" ConditionalExpression() ] +} + +void ConstantExpression() : {} +{ + ConditionalExpression() +} + +void LogicalORExpression() : {} +{ + LogicalANDExpression() [ "||" (LOOKAHEAD(3) LogicalORExpression() | NestedLogicalExpression()) ] +} + +void LogicalANDExpression() : {} +{ + InclusiveORExpression() [ "&&" (LOOKAHEAD(3) LogicalANDExpression() | NestedLogicalExpression()) ] +} + +void InclusiveORExpression() : {} +{ + ExclusiveORExpression() [ "|" InclusiveORExpression() ] +} + +void ExclusiveORExpression() : {} +{ + ANDExpression() [ "^" ExclusiveORExpression() ] +} + +void ANDExpression() : {} +{ + EqualityExpression() [ "&" ANDExpression() ] +} + +void EqualityExpression() : {} +{ + RelationalExpression() [ ( "==" | "!=" ) EqualityExpression() ] +} + +void RelationalExpression() : {} +{ + ShiftExpression() [ ( "<" | ">" | "<=" | ">=" ) RelationalExpression() ] +} + +void ShiftExpression() : {} +{ + AdditiveExpression() [ ( "<<" | ">>" ) ShiftExpression() ] +} + +void AdditiveExpression() : {} +{ + MultiplicativeExpression() [ ( "+" | "-" ) AdditiveExpression() ] +} + +void MultiplicativeExpression() : {} +{ + CastExpression() [ ( "*" | "/" | "%" ) MultiplicativeExpression() ] +} + +void CastExpression() : {} +{ + LOOKAHEAD("(" TypeName() ")" CastExpression() ) "(" TypeName() ")" CastExpression() | +// LOOKAHEAD(3) "(" TypeSpecifier() ")" CastExpression() | + UnaryExpression() +} + +void UnaryExpression() : {} +{ + ( LOOKAHEAD(3) PostfixExpression() | + "++" UnaryExpression() | + "--" UnaryExpression() | + UnaryOperator() CastExpression() | + ( LOOKAHEAD(UnaryExpression() ) UnaryExpression() | "(" TypeName() ")" ) ) +} + +void UnaryOperator() : {} +{ + ( "&" | "*" | "+" | "-" | "~" | "!" ) +} + +void PostfixExpression() : {} +{ + | /* mjh */ + PrimaryExpression() ( "[" Expression() "]" | + "(" [ LOOKAHEAD(ArgumentExpressionList() ) ArgumentExpressionList() ] ")" | + "." | + "->" | + "++" | + "--" )* +} + +void PrimaryExpression() : {} +{ + ( ObjCIDENT() | + Constant() | + "(" Expression() ")" | + MessageExpression() | + SelectorExpression() | + ProtocolExpression() | + EncodeExpression() ) +} + +void MessageExpression() : {} +{ + Receiver() MessageSelector() +} + +void Receiver() : {} +{ + LOOKAHEAD(2) Expression() // catches 'self' and 'super' as ident + | TypeDefedIDENT() +} + +void TypeDefedIDENT() : {} +{ + | | | | +} + +void MessageSelector() : {} +{ + LOOKAHEAD(2) (KeywordArgument())+ + | +} + +void KeywordArgument() : {} +{ + LOOKAHEAD(3) Expression() + | + | LOOKAHEAD(2) + | Expression() +} + +void ArgumentExpressionList() : {} +{ + AssignmentExpression() ( "," AssignmentExpression() )* +} + +void Constant() : {} +{ + | | | | | +} + diff --git a/pmd-objectivec/pom.xml b/pmd-objectivec/pom.xml new file mode 100644 index 0000000000..a9afe7428c --- /dev/null +++ b/pmd-objectivec/pom.xml @@ -0,0 +1,96 @@ + + + 4.0.0 + pmd-objectivec + PMD Objective-C + + + net.sourceforge.pmd + pmd + 5.2.4-SNAPSHOT + + + + ${basedir}/../pmd-core + + + + + + maven-resources-plugin + + false + + ${*} + + + + + + org.apache.maven.plugins + maven-antrun-plugin + true + + + generate-sources + generate-sources + + + + + + + + + + run + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-javacc-generated-sources + + add-source + + + + ${project.build.directory}/generated-sources/javacc + + + + + + + + org.apache.maven.plugins + maven-site-plugin + + ${project.build.directory}/generated-xdocs + + + + + + + net.sourceforge.pmd + pmd-core + + + + junit + junit + test + + + net.sourceforge.pmd + pmd-test + test + + + diff --git a/pmd-objectivec/src/main/ant/alljavacc.xml b/pmd-objectivec/src/main/ant/alljavacc.xml new file mode 100644 index 0000000000..5396813b9a --- /dev/null +++ b/pmd-objectivec/src/main/ant/alljavacc.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCLanguage.java b/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCLanguage.java new file mode 100644 index 0000000000..961a978b9f --- /dev/null +++ b/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCLanguage.java @@ -0,0 +1,18 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package net.sourceforge.pmd.cpd; + + +/** + * Defines the Language module for Objective-C + */ +public class ObjectiveCLanguage extends AbstractLanguage { + + /** + * Creates a new instance of {@link ObjectiveCLanguage} with the default extensions for Objective-C files. + */ + public ObjectiveCLanguage() { + super("Objective-C", "objectivec", new ObjectiveCTokenizer(), ".h", ".m"); + } +} diff --git a/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizer.java b/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizer.java new file mode 100644 index 0000000000..7de6c858d8 --- /dev/null +++ b/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizer.java @@ -0,0 +1,44 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package net.sourceforge.pmd.cpd; + +import java.io.Reader; +import java.io.StringReader; + +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersionHandler; +import net.sourceforge.pmd.lang.TokenManager; +import net.sourceforge.pmd.lang.ast.TokenMgrError; +import net.sourceforge.pmd.lang.objectivec.ObjectiveCLanguageModule; +import net.sourceforge.pmd.lang.objectivec.ast.Token; + +import org.apache.commons.io.IOUtils; + +public class ObjectiveCTokenizer implements Tokenizer { + + public void tokenize(SourceCode sourceCode, Tokens tokenEntries) { + StringBuilder buffer = sourceCode.getCodeBuffer(); + Reader reader = null; + try { + LanguageVersionHandler languageVersionHandler = LanguageRegistry.getLanguage(ObjectiveCLanguageModule.NAME) + .getDefaultVersion().getLanguageVersionHandler(); + reader = new StringReader(buffer.toString()); + TokenManager tokenManager = languageVersionHandler.getParser( + languageVersionHandler.getDefaultParserOptions()).getTokenManager(sourceCode.getFileName(), reader); + Token currentToken = (Token) tokenManager.getNextToken(); + while (currentToken.image.length() > 0) { + tokenEntries.add(new TokenEntry(currentToken.image, sourceCode.getFileName(), currentToken.beginLine)); + currentToken = (Token) tokenManager.getNextToken(); + } + tokenEntries.add(TokenEntry.getEOF()); + System.err.println("Added " + sourceCode.getFileName()); + } catch (TokenMgrError err) { + err.printStackTrace(); + System.err.println("Skipping " + sourceCode.getFileName() + " due to parse error"); + tokenEntries.add(TokenEntry.getEOF()); + } finally { + IOUtils.closeQuietly(reader); + } + } +} diff --git a/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCHandler.java b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCHandler.java new file mode 100644 index 0000000000..dc19effdff --- /dev/null +++ b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCHandler.java @@ -0,0 +1,25 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package net.sourceforge.pmd.lang.objectivec; + +import net.sourceforge.pmd.lang.AbstractLanguageVersionHandler; +import net.sourceforge.pmd.lang.Parser; +import net.sourceforge.pmd.lang.ParserOptions; +import net.sourceforge.pmd.lang.rule.RuleViolationFactory; + +/** + * Implementation of LanguageVersionHandler for the Objective-C Language. + */ +public class ObjectiveCHandler extends AbstractLanguageVersionHandler { + + @Override + public RuleViolationFactory getRuleViolationFactory() { + throw new UnsupportedOperationException("getRuleViolationFactory() is not supported for Objective-C"); + } + + @Override + public Parser getParser(ParserOptions parserOptions) { + return new ObjectiveCParser(parserOptions); + } +} diff --git a/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCLanguageModule.java b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCLanguageModule.java new file mode 100644 index 0000000000..37c53819d5 --- /dev/null +++ b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCLanguageModule.java @@ -0,0 +1,25 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package net.sourceforge.pmd.lang.objectivec; + +import net.sourceforge.pmd.lang.BaseLanguageModule; + +/** + * Implementation of the Objective-C Language Module. + */ +public class ObjectiveCLanguageModule extends BaseLanguageModule { + + /** The name, that can be used to display the language in UI. */ + public static final String NAME = "Objective-C"; + /** The internal name. */ + public static final String TERSE_NAME = "objectivec"; + + /** + * Creates a new instance of {@link ObjectiveCLanguageModule} with the default file extensions for Objective-C. + */ + public ObjectiveCLanguageModule() { + super(NAME, null, TERSE_NAME, null, "h", "m"); + addVersion("", new ObjectiveCHandler(), true); + } +} diff --git a/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCParser.java b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCParser.java new file mode 100644 index 0000000000..6e1b87ee3e --- /dev/null +++ b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCParser.java @@ -0,0 +1,49 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package net.sourceforge.pmd.lang.objectivec; + +import java.io.Reader; +import java.util.Map; + +import net.sourceforge.pmd.lang.AbstractParser; +import net.sourceforge.pmd.lang.ParserOptions; +import net.sourceforge.pmd.lang.TokenManager; +import net.sourceforge.pmd.lang.ast.AbstractTokenManager; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.ParseException; + +/** + * Adapter for the Objective-C Parser. + */ +public class ObjectiveCParser extends AbstractParser { + + /** + * Creates a new Objective-C Parser. + * @param parserOptions the options + */ + public ObjectiveCParser(ParserOptions parserOptions) { + super(parserOptions); + } + + @Override + public TokenManager createTokenManager(Reader source) { + return new ObjectiveCTokenManager(source); + } + + @Override + public boolean canParse() { + return false; + } + + @Override + public Node parse(String fileName, Reader source) throws ParseException { + AbstractTokenManager.setFileName(fileName); + throw new UnsupportedOperationException("parse(Reader) is not supported for Objective-C"); + } + + @Override + public Map getSuppressMap() { + throw new UnsupportedOperationException("getSuppressMap() is not supported for Objective-C"); + } +} diff --git a/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCTokenManager.java b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCTokenManager.java new file mode 100644 index 0000000000..79942c43c4 --- /dev/null +++ b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCTokenManager.java @@ -0,0 +1,34 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package net.sourceforge.pmd.lang.objectivec; + +import java.io.Reader; + +import net.sourceforge.pmd.lang.TokenManager; +import net.sourceforge.pmd.lang.ast.SimpleCharStream; +import net.sourceforge.pmd.lang.objectivec.ast.ObjectiveCParserTokenManager; + +/** + * Objective-C Token Manager implementation. + */ +public class ObjectiveCTokenManager implements TokenManager { + private final ObjectiveCParserTokenManager tokenManager; + + /** + * Creates a new Objective-C Token Manager from the given source code. + * @param source the source code + */ + public ObjectiveCTokenManager(Reader source) { + tokenManager = new ObjectiveCParserTokenManager(new SimpleCharStream(source)); + } + + public Object getNextToken() { + return tokenManager.getNextToken(); + } + + @Override + public void setFileName(String fileName) { + ObjectiveCParserTokenManager.setFileName(fileName); + } +} diff --git a/pmd-objectivec/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-objectivec/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language new file mode 100644 index 0000000000..6154027550 --- /dev/null +++ b/pmd-objectivec/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.cpd.ObjectiveCLanguage diff --git a/pmd-objectivec/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-objectivec/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..65daa818c8 --- /dev/null +++ b/pmd-objectivec/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.objectivec.ObjectiveCLanguageModule diff --git a/pmd-objectivec/src/site/markdown/index.md b/pmd-objectivec/src/site/markdown/index.md new file mode 100644 index 0000000000..032f2e53ac --- /dev/null +++ b/pmd-objectivec/src/site/markdown/index.md @@ -0,0 +1,3 @@ +# PMD Objective-C + +Only CPD is supported. There are no PMD rules for Objective-C. diff --git a/pmd-objectivec/src/site/site.xml b/pmd-objectivec/src/site/site.xml new file mode 100644 index 0000000000..83a31da3db --- /dev/null +++ b/pmd-objectivec/src/site/site.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/pmd-objectivec/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java b/pmd-objectivec/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java new file mode 100644 index 0000000000..5eb0673b03 --- /dev/null +++ b/pmd-objectivec/src/test/java/net/sourceforge/pmd/LanguageVersionTest.java @@ -0,0 +1,27 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package net.sourceforge.pmd; + +import java.util.Arrays; +import java.util.Collection; + +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.objectivec.ObjectiveCLanguageModule; + +import org.junit.runners.Parameterized.Parameters; + +public class LanguageVersionTest extends AbstractLanguageVersionTest { + + public LanguageVersionTest(String name, String terseName, String version, LanguageVersion expected) { + super(name, terseName, version, expected); + } + + @Parameters + public static Collection data() { + return Arrays.asList(new Object[][] { + { ObjectiveCLanguageModule.NAME, ObjectiveCLanguageModule.TERSE_NAME, "", LanguageRegistry.getLanguage(ObjectiveCLanguageModule.NAME).getDefaultVersion() } + }); + } +} diff --git a/pmd-objectivec/src/test/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizerTest.java b/pmd-objectivec/src/test/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizerTest.java new file mode 100644 index 0000000000..c8b2e72743 --- /dev/null +++ b/pmd-objectivec/src/test/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizerTest.java @@ -0,0 +1,40 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package net.sourceforge.pmd.cpd; + +import java.io.IOException; + +import net.sourceforge.pmd.testframework.AbstractTokenizerTest; +import net.sourceforge.pmd.testframework.StreamUtil; + +import org.junit.Before; +import org.junit.Test; + + +public class ObjectiveCTokenizerTest extends AbstractTokenizerTest { + + private static final String FILENAME = "AFHTTPRequestOperation.m"; + + @Before + @Override + public void buildTokenizer() { + this.tokenizer = new ObjectiveCTokenizer(); + this.sourceCode = new SourceCode(new SourceCode.StringCodeLoader(this.getSampleCode(), FILENAME)); + } + + @Override + public String getSampleCode() { + return StreamUtil.toString(ObjectiveCTokenizer.class.getResourceAsStream(FILENAME)); + } + + @Test + public void tokenizeTest() throws IOException { + this.expectedTokenCount = 884; + super.tokenizeTest(); + } + + public static junit.framework.Test suite() { + return new junit.framework.JUnit4TestAdapter(ObjectiveCTokenizerTest.class); + } +} diff --git a/pmd-objectivec/src/test/resources/net/sourceforge/pmd/cpd/AFHTTPRequestOperation.m b/pmd-objectivec/src/test/resources/net/sourceforge/pmd/cpd/AFHTTPRequestOperation.m new file mode 100644 index 0000000000..1f182b49d7 --- /dev/null +++ b/pmd-objectivec/src/test/resources/net/sourceforge/pmd/cpd/AFHTTPRequestOperation.m @@ -0,0 +1,209 @@ +// This sample file was copied from the AFNetworking GitHub project. (https://github.com/AFNetworking/AFNetworking) +// Original source file: https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFHTTPRequestOperation.m +// +// AFHTTPRequestOperation.m +// +// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFHTTPRequestOperation.h" + +static dispatch_queue_t http_request_operation_processing_queue() { + static dispatch_queue_t af_http_request_operation_processing_queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_http_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.http-request.processing", DISPATCH_QUEUE_CONCURRENT); + }); + + return af_http_request_operation_processing_queue; +} + +static dispatch_group_t http_request_operation_completion_group() { + static dispatch_group_t af_http_request_operation_completion_group; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_http_request_operation_completion_group = dispatch_group_create(); + }); + + return af_http_request_operation_completion_group; +} + +#pragma mark - + +@interface AFURLConnectionOperation () +@property (readwrite, nonatomic, strong) NSURLRequest *request; +@property (readwrite, nonatomic, strong) NSURLResponse *response; +@end + +@interface AFHTTPRequestOperation () +@property (readwrite, nonatomic, strong) NSHTTPURLResponse *response; +@property (readwrite, nonatomic, strong) id responseObject; +@property (readwrite, nonatomic, strong) NSError *responseSerializationError; +@property (readwrite, nonatomic, strong) NSRecursiveLock *lock; +@end + +@implementation AFHTTPRequestOperation +@dynamic lock; + +- (instancetype)initWithRequest:(NSURLRequest *)urlRequest { + self = [super initWithRequest:urlRequest]; + if (!self) { + return nil; + } + + self.responseSerializer = [AFHTTPResponseSerializer serializer]; + + return self; +} + +- (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { + NSParameterAssert(responseSerializer); + + [self.lock lock]; + _responseSerializer = responseSerializer; + self.responseObject = nil; + self.responseSerializationError = nil; + [self.lock unlock]; +} + +- (id)responseObject { + [self.lock lock]; + if (!_responseObject && [self isFinished] && !self.error) { + NSError *error = nil; + self.responseObject = [self.responseSerializer responseObjectForResponse:self.response data:self.responseData error:&error]; + if (error) { + self.responseSerializationError = error; + } + } + [self.lock unlock]; + + return _responseObject; +} + +- (NSError *)error { + if (_responseSerializationError) { + return _responseSerializationError; + } else { + return [super error]; + } +} + +#pragma mark - AFHTTPRequestOperation + +- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + // completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-retain-cycles" +#pragma clang diagnostic ignored "-Wgnu" + self.completionBlock = ^{ + if (self.completionGroup) { + dispatch_group_enter(self.completionGroup); + } + + dispatch_async(http_request_operation_processing_queue(), ^{ + if (self.error) { + if (failure) { + dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(self, self.error); + }); + } + } else { + id responseObject = self.responseObject; + if (self.error) { + if (failure) { + dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(self, self.error); + }); + } + } else { + if (success) { + dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ + success(self, responseObject); + }); + } + } + } + + if (self.completionGroup) { + dispatch_group_leave(self.completionGroup); + } + }); + }; +#pragma clang diagnostic pop +} + +#pragma mark - AFURLRequestOperation + +- (void)pause { + [super pause]; + + u_int64_t offset = 0; + if ([self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey]) { + offset = [(NSNumber *)[self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] unsignedLongLongValue]; + } else { + offset = [(NSData *)[self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey] length]; + } + + NSMutableURLRequest *mutableURLRequest = [self.request mutableCopy]; + if ([self.response respondsToSelector:@selector(allHeaderFields)] && [[self.response allHeaderFields] valueForKey:@"ETag"]) { + [mutableURLRequest setValue:[[self.response allHeaderFields] valueForKey:@"ETag"] forHTTPHeaderField:@"If-Range"]; + } + [mutableURLRequest setValue:[NSString stringWithFormat:@"bytes=%llu-", offset] forHTTPHeaderField:@"Range"]; + self.request = mutableURLRequest; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.responseSerializer = [decoder decodeObjectOfClass:[AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFHTTPRequestOperation *operation = [super copyWithZone:zone]; + + operation.responseSerializer = [self.responseSerializer copyWithZone:zone]; + operation.completionQueue = self.completionQueue; + operation.completionGroup = self.completionGroup; + + return operation; +} + +@end \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3380601831..cc1a7621ae 100644 --- a/pom.xml +++ b/pom.xml @@ -864,6 +864,7 @@ pmd-java pmd-javascript pmd-jsp + pmd-objectivec pmd-php pmd-plsql pmd-ruby