diff options
author | Tor Norbye <tnorbye@google.com> | 2013-08-29 10:40:28 -0700 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2013-08-29 10:40:35 -0700 |
commit | 932259520ebaedeb2ccf4b7594bad50c700963d7 (patch) | |
tree | 2b52334b8a019d9652625b1432deac11a7c4b31e /plugins | |
parent | e47d04f1f804b9e725b768725da436af6788f19b (diff) | |
download | idea-932259520ebaedeb2ccf4b7594bad50c700963d7.tar.gz |
Snapshot 1c7917945d4706cdbb32b599f81abd05c0251e32 from idea/132.27 branch of git://git.jetbrains.org/idea/community.git
1c79179: 2013-08-29 Yann Cébron - DevKit: smart EP "implementation" highlighting/completion
f3a83bc: 2013-08-29 Roman Shevchenko - IDEA-112827 (NPE in error reporter)
464a45b: 2013-08-29 Anna Kozlova - move pin button to the right side of popup (IDEA-112435)
1635be8: 2013-08-29 Yann Cébron - Dom EPs: add some <with> tags
6bcac42: 2013-08-29 Roman Shevchenko - IDEA-112824 (suppress SVN logging for normal use)
16d0ba4: 2013-08-29 Anna Kozlova - NPE
285509c4c: 2013-08-28 Max Medvedev - IDEA-111110 Groovy: Introduce Field Refactoring doesn't suggest to choose destination class
5415af1: 2013-08-28 Yann Cébron - Merge remote-tracking branch 'origin/master'
8462f3b: 2013-08-28 Dmitry Jemerov - cleanup
2ad6100: 2013-08-28 Yann Cébron - Merge remote-tracking branch 'origin/master'
9de8bfc: 2013-08-28 Dmitry Jemerov - Merge branch 'master' of git://github.com/niktrop/intellij-community into pull92
6315415: 2013-08-28 Dmitry Jemerov - cleanup
7281b73: 2013-08-28 Dmitry Jemerov - Merge branch 'cjfm3' of git://github.com/max-kammerer/intellij-community into pull87
6e31350: 2013-08-28 Dmitry Jemerov - fix couple of issues with https://github.com/JetBrains/intellij-community/pull/94
3695aa0: 2013-08-28 Dmitry Jemerov - Merge branch 'master' of git://github.com/asedunov/intellij-community into pull94
ce2c15f: 2013-08-28 Yann Cébron - fix javadoc @see link
6794ead: 2013-08-28 Anna Kozlova - skip adverts when server doesn't accept provided info
851da00: 2013-08-28 Anna Kozlova - skip advs in tests and headless mode
cddbc28: 2013-08-28 Anna Kozlova - EA-48802 - assert: JavaFileManagerBase.findClass
1eafaae: 2013-08-28 Anna Kozlova - EA-49147 - NPE: UnusedDeclarationInspection.isReadObjectMethod
f1a2040: 2013-08-28 Anna Kozlova - EA-49155 - NPE: JavaChangeSignatureDialog.doCalculateSignature
2684709: 2013-08-28 Dmitry Jemerov - MalformedFormatStringInspectionTest fixed
ce8088e: 2013-08-28 Evgeny Gerashchenko - Removed extra checking for duplicate annotations in one file. It is performed when reading file anyway.
9d4c7ca: 2013-08-28 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
2fd8641: 2013-08-28 Eugene Kudelevsky - add possibility to setup lookup element for XML attribute values; IDEA-102167 layout_* attributes should go first
3b065b1: 2013-08-28 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
d8d9168: 2013-08-28 Dmitry Trofimov - Libs update.
28a95a6: 2013-08-28 Alexey Kudravtsev - compilation
d4a0d69: 2013-08-28 Alexey Kudravtsev - notnull
f9f8006: 2013-08-28 Alexey Kudravtsev - moved to appropriate package
4d058f8: 2013-08-28 Alexey Kudravtsev - cleanup
111301a: 2013-08-28 Alexey Kudravtsev - extra method
01e59b0: 2013-08-28 Alexey Kudravtsev - doc
da966ae: 2013-08-28 Alexey Kudravtsev - cleanup
7503616: 2013-08-28 Alexey Kudravtsev - add method to javaversionservice
7d6de4f: 2013-08-28 Alexey Kudravtsev - notnull
dee081f: 2013-08-27 Alexey Kudravtsev - statistics
dd327c0: 2013-08-28 Dmitry Trofimov - Focus fixes.
7231931: 2013-08-28 Vladimir Krivosheev - WEB-8988 Return "open in browser" in context menu
2102255: 2013-08-28 Roman Shevchenko - app: install-over range extended for next EAPs
ddad3bb: 2013-08-28 Dmitry Avdeev - IDEA-112728 Can't create new task from tasks menu in toolbar: no need to fix lost typing for Go To Task
a8160c2: 2013-08-28 Dmitry Avdeev - typo
7328cbd: 2013-08-28 Dmitry Avdeev - cleanup
abbdb5a: 2013-08-28 Dmitry Avdeev - cleanup
a5c8503: 2013-08-28 Dmitry Avdeev - do not allow empty task name
e8daf37: 2013-08-28 Dmitry Avdeev - simplified
dff28d3: 2013-08-28 Dmitry Avdeev - simplified
38513a8: 2013-08-28 Dmitry Avdeev - cleanup
c09dd55: 2013-08-28 Vladimir Krivosheev - VariablesGroup — avoid array copy
8f3c91f: 2013-08-28 Roman Shevchenko - java: correct character escaping in decompiler
9a19e30: 2013-08-28 Dmitry Trofimov - Fixed hiding and activating of the terminal (PY-10669).
df672ba: 2013-08-28 Dmitry Trofimov - Terminal system settings refactored.
4653b54: 2013-08-28 Anna Kozlova - unknown features equality fixed
2896270: 2013-08-28 Anna Kozlova - ensure read access
ccff3af: 2013-08-28 Anna Kozlova - suggest to download plugins by unknown run configurations
9c8a3d2: 2013-08-28 Konstantin Bulenkov - include os.arch and jdk build number in about
861984c: 2013-08-28 Denis Fokin - IDEA-108265. We should not do anything if an empty array is passed.
a6b3441: 2013-08-28 Sergey Simonchik - EA-49063 - AIOOBE: ScriptRunnerUtil$ScriptOutput.onTextAvailable
845ee5c: 2013-08-28 Vladimir Krivosheev - value nullability
36121a2: 2013-08-28 Konstantin Kolosovsky - Merge branch 'svn1_8_new'
1515b70: 2013-08-28 Mikhail Golubev - Merge remote-tracking branch 'origin/master'
33d684e0: 2013-08-28 Mikhail Golubev - IDEA-110012 Not all Redmine Issues Available on "Open Task" (Limited to 100?)
39899b1: 2013-08-28 Bas Leijdekkers - IDEA-112782 (Change signature dialog shows unexpected 'cannot resolve symbol' message)
e469928: 2013-08-28 Anna Kozlova - accept test config methods in non-test classes (IDEA-112537)
b53abed: 2013-08-27 Anna Kozlova - fix typo
3074f49: 2013-08-28 Dmitry Avdeev - IDEA-112781 Open YouTrack task: Create changelist doesn't work with SVN ?
43c8897: 2013-08-27 Dmitry Avdeev - cleanup
ff6217a: 2013-08-28 Kirill Likhodedov - Annotate overriding methods
8e2d0e1: 2013-08-28 Roman Shevchenko - terminal: platform's Guava should be good enough for the plugin
2055780: 2013-08-27 Roman Shevchenko - EA-49123 (do not load extensions from static initializer)
116dc30: 2013-08-27 Roman Shevchenko - EA-49235 (check proxy port)
2b4f96f: 2013-08-27 Roman Shevchenko - EA-49235 (code readability)
ed03bca: 2013-08-27 Roman Shevchenko - EA-49142 (NPE, cleanup)
e6053d0: 2013-08-27 Roman Shevchenko - EA-49102 (face user with printing errors)
a748474: 2013-08-28 Vladimir Krivosheev - 4.1.0. update netty (now it is not patched build, https://github.com/netty/netty/pull/1762)
3c60901: 2013-08-28 Konstantin Bulenkov - better selection for mixed languages
20decc3: 2013-08-28 Konstantin Bulenkov - fix selection for files with multiple languages
878ad26: 2013-08-27 Max Medvedev - IDEA-111100 Groovy: Introduce Variable/Parameter Refactorings don't suggest to replace occurrences if applied to expressions inside code blocks
8a60662: 2013-08-27 Max Medvedev - IDEA-110981 Groovy: "Split into declaration and assignment" intention leaves unnecessary "=" if applied to closures
77912c1: 2013-08-27 Max Medvedev - IDEA-111101 Groovy: In-Place Introduce Field: Alt+I mnemonic doesn't work in the refactoring preview
088f68e: 2013-08-27 Max Medvedev - IDEA-111027 Groovy: In-Place Introduce Variable: PIEAE at GrInplaceIntroducer.<init> on introducing a variable within one-line method/closure
a2210a4: 2013-08-27 Aleksei Sedunov - Extract inheritor candidate check into separate InheritanceChecker interface
f40be5e: 2013-08-27 Alexander Zolotov - Filter moduleAwareConfigurables by module
cf7704f: 2013-08-26 Alexander Zolotov - WEB-6452 SASS suggests functions at the top, instead of property values
165ccf7: 2013-08-27 Konstantin Kolosovsky - IDEA-94942 Fixed diff, annotate errors in history view after rename/move
7b6396a: 2013-08-27 Dmitry Jemerov - branch number 132
e176d25: 2013-08-27 Sergey Evdokimov - IDEA-112754 Maven import: NCDFE for org/jetbrains/plugins/groovy/util/ClassInstanceCache
43c49f6: 2013-08-27 Sergey Evdokimov - IDEA-112754 Maven import: NCDFE for org/jetbrains/plugins/groovy/util/ClassInstanceCache
377dd45: 2013-08-27 Konstantin Bulenkov - fix memory leak
203fb69: 2013-08-27 Sergey Simonchik - WEB-9011 Karma plugin ignored tests
3b896f2: 2013-08-27 Sergey Evdokimov - Make project unignored when new module is created by ignored project
9ae29c6: 2013-08-27 Sergey Evdokimov - Remove maven project from project tree when user deletes module. +review CR-IC-2084
0f0f39e: 2013-08-27 Vladimir Krivosheev - cleanup, Overrides
3dddec4: 2013-08-27 Vladimir Krivosheev - cleanup
a2fff55: 2013-08-27 Aleksey Pivovarov - Github: Add API function for loading Commit Comments
9bd1660: 2013-08-27 Aleksey Pivovarov - fix IndexOutOfBoundsException on inserting empty collection to empty model
46b8998: 2013-08-27 Nadya Zabrodina - exception during annotate copied file fixed
66640b2: 2013-08-27 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
c597bcc: 2013-08-27 Sergey Evdokimov - Remove maven project from project tree when user deletes module. +review CR-IC-2084
33e932a: 2013-08-27 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
7cc9cca: 2013-08-27 Vladislav.Soroka - IDEA-79466 gradle support should generate web module configuration
42e649d: 2013-08-27 Dmitry Jemerov - better names for couple of new classes added to API
5c70eaf: 2013-08-27 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
2e0f1fa: 2013-08-27 Dmitry Trofimov - Update lib.
dcc2c4b: 2013-08-27 Dmitry Trofimov - Override isRetina.
05b716b: 2013-08-27 nik - processing dependencies in JPS: @NotNull annotations and javadoc added
6314b6e: 2013-08-27 Dmitry Trofimov - Fixed for Retina.
9d1886c: 2013-08-27 Dmitry Avdeev - navigatable xsd documentation
0f389c5: 2013-08-27 Vladimir Krivosheev - hide internal class XValuePresenterAdapter
a0386eb: 2013-08-27 Mikhail Golubev - Merge remote-tracking branch 'origin/master'
53248dc: 2013-08-27 Vladimir Krivosheev - fix createPresenter
d0355b3: 2013-08-27 Sergey Evdokimov - Remove maven project from project tree when user deletes module. +review CR-IC @Anton.Makeev
fb48f62: 2013-08-27 Sergey Evdokimov - Optimization of MavenProjectsTree.isManagedFile()
6b123aa: 2013-08-27 nik - JPS dependencies enumerator: convenient method added
d4579e1: 2013-08-27 Dmitry Avdeev - UrlPsiReference promoted
3b1feea: 2013-08-27 Dmitry Avdeev - cleanup
ee18443: 2013-08-27 Dmitry Avdeev - cleanup
ca2484e: 2013-08-27 Dmitry Avdeev - cleanup
c9e045b: 2013-08-27 Vladimir Krivosheev - 1) methods "void setPresentation(@NonNls String name, @Nullable Icon icon, @NonNls @Nullable String type, @NonNls @NotNull String separator, @NonNls @NotNull String value, boolean hasChildren);" and "void setPresentation(@NonNls String name, @Nullable Icon icon, @NonNls @Nullable String type, @NonNls @NotNull String value, boolean hasChildren);"
b38d58a: 2013-08-27 Konstantin Kolosovsky - IDEA-94942 Fixed treating svn client warnings as errors
8cab12c: 2013-08-27 Dmitry Jemerov - separate UI and non-UI parts of MalformedFormatStringInspection
93e3fa6: 2013-08-20 Jason Holmes - Custom "Malformed format string" inspection
b9f6fde: 2013-08-27 nik - actions to mark/unmark roots in Project View refactored to support custom root types
0455e46: 2013-08-27 Aleksey Pivovarov - Github: remove useless listener
7f40613: 2013-08-27 Aleksey Pivovarov - Github: reset User on token change
c8a5402: 2013-08-27 Aleksey Pivovarov - Github: change layout
89769be: 2013-08-27 Aleksey Pivovarov - Github: add test
0d8ab04: 2013-08-27 Aleksey Pivovarov - Github: comment
cba103c: 2013-08-27 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
d6ca049: 2013-08-27 Anna Kozlova - allow autoPopup after custom symbols (IDEA-112571)
a70c338: 2013-08-27 Konstantin Bulenkov - IDEA-110846 File Structure pop-up doesn't work properly for template languages
4896775: 2013-08-27 Konstantin Bulenkov - recent files duplicates
05835fa: 2013-08-27 Fedor Korotkov - WEB-6328 Add support for HTML5 <main> element
2407d7b: 2013-08-27 Anna Kozlova - allow to call getValue without readAction as it was before
29cb25d: 2013-08-27 Dmitry Avdeev - fixing the leak
7d3932a: 2013-08-27 Dmitry Avdeev - IDEA-112708 Static classes in JSP class block are considered an error
f6dbce2: 2013-08-26 Mikhail Golubev - IDEA-112605 Task management: can't add Generic server: NoClassDefFoundError: XPathFileType
82c1dc1: 2013-08-27 Konstantin Kolosovsky - Merge branch 'svn1_8_new'
08d46f1: 2013-08-27 Alexey Kudravtsev - highlightVisitor moves and cleanup
e0fec9d: 2013-08-27 Alexey Kudravtsev - Merge remote-tracking branch 'origin/master'
e9b1dfc: 2013-08-27 Anna Kozlova - dumb smart lambda completion (IDEA-112553)
6da30a4: 2013-08-26 Alexey Kudravtsev - calculate column/offset: optimisation of the no-tabs case
c0990c4: 2013-08-26 Alexey Kudravtsev - race conditions?
135e250: 2013-08-26 Alexey Kudravtsev - notnull, cleanup
75b28ba: 2013-08-26 Alexey Kudravtsev - file was not rehighlighted on some changes
5647d35: 2013-08-26 Sergey Evdokimov - Make myManagedFilesPaths a Set to avoid duplication.
680dd76: 2013-08-27 Aleksey Pivovarov - Github: fix memory leak on Exception in setUp();
e3092b7: 2013-08-27 Dmitry Avdeev - IDEA-112611 Task management: DVCS: closing a task fails: "Cannot delete the branch master"
d12d4fe: 2013-08-27 Anna Kozlova - logging for EA-49099 - PIEAE: PsiAnchor$StubIndexReference.getStartOffset
3cc53fe: 2013-08-27 Anna Kozlova - revert changes in api
10fbef9: 2013-08-27 Roman Shevchenko - Test data updated
a5455ba: 2013-08-27 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
8ed3ae8: 2013-08-27 Dmitry Trofimov - Updated lib.
e6df583: 2013-08-27 Dmitry Trofimov - Added guava lib to terminal.
1853090: 2013-08-27 Dmitry Trofimov - Draw image Retina support.
a7ebcaf: 2013-08-27 Dmitry Trofimov - Open session action moved to constructor.
5176bfa: 2013-08-26 Sergey Evdokimov - Fix maven tests
291f740: 2013-08-26 Vladimir Krivosheev - we must check if any port free too
18d4be6: 2013-08-26 Vassiliy Kudryashov - IDEA-70769 Settings panel: increase speed of scrollbars
0c76aca: 2013-08-26 Vassiliy Kudryashov - IDEA-112524 Working directory for default rake tasks is changed to $MODULE_DIR$ after project's reopening
e2b7f6b: 2013-08-26 Konstantin Bulenkov - pattern dependent delay
1916142: 2013-08-26 Vladimir Krivosheev - cleanup
3da9dcd: 2013-08-26 Vladimir Krivosheev - done: custom binary request handler
e02eba9: 2013-08-26 Aleksey Pivovarov - Github: do not produce dozens of notifications
8e41d59: 2013-08-26 Aleksey Pivovarov - Simplify
78c800d: 2013-08-26 Aleksey Pivovarov - Github: respect 'max' parameter
3afb19f: 2013-08-26 Maxim.Mossienko - proper versioning of stub index when persistent enumerator version changes
c853e9e: 2013-08-26 Eugene Kudelevsky - IDEA-112376 add "importFilter" extension point to force using FQN when importing class
31281a9: 2013-08-26 Kirill Likhodedov - [git] Don't write empty lines to LOG.debug.
493b9fb: 2013-08-26 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
60384ba: 2013-08-26 Dmitry Trofimov - Enable Run local terminal action for Windows.
bfa8e61: 2013-08-26 Dmitry Jemerov - stupid typo fixed
04a76c3: 2013-08-26 Anna Kozlova - IDEA-112555 Bad code is green with method references on instance
8f5139d: 2013-08-26 Denis Fokin - IDEA-108265. Now user is asked whether the project should be opened in a new frame.
da958ab: 2013-08-26 Natalia Ukhorskaya - Decompile chars, bytes and shorts correctly
8008709: 2013-08-26 Roman Shevchenko - java: incorrect annotation decoding fixed
9f07ea5: 2013-08-26 Roman Shevchenko - logging
e07c905: 2013-08-26 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
e81832d: 2013-08-26 Dmitry Trofimov - This update fixes pty on windows.
e36607b: 2013-08-26 Konstantin Bulenkov - new renderer
9ea47a5: 2013-08-26 nik - source roots editors: obtain icons for content tree from extension
d52dfb8: 2013-08-26 nik - constants moved
710d3ba: 2013-08-26 niktrop - Code style fixed
aa8832d: 2013-08-26 Sergey Simonchik - code style: two subsequent ifs merged to reduce inner indent
ed88487: 2013-08-26 Konstantin Kolosovsky - IDEA-94942 Provide detailed error messages to user instead of general ones
e0df7a3: 2013-08-26 Vassiliy Kudryashov - IDEA-112524 Working directory for default rake tasks is changed to $MODULE_DIR$ after project's reopening
e60a55b: 2013-08-26 nik - source roots editors refactored: root type specific UI moved to extension
928ea20: 2013-08-26 Mikhail Golubev - Merge remote-tracking branch 'origin/master'
70d526d: 2013-08-26 Anna Kozlova - guardedBy itself support (IDEA-112565)
2e23cb8: 2013-08-26 Anna Kozlova - restore bytecode viewer for java classes
89dffc9: 2013-08-26 Anna Kozlova - cal property name: accept without any other checks non-letter prefixes (IDEA-112585)
bab044f: 2013-08-26 Vladimir Krivosheev - restore Alarm.cancelRequest
dd817dc: 2013-08-26 Sergey Simonchik - simplification
7046dc4: 2013-08-26 niktrop - Some refactoring for reusing GenerateEquals UI in scala plugin
44e4219: 2013-08-26 Sergey Evdokimov - IDEA-112529 Maven: code completion could work in file path value with property references
867be29: 2013-08-26 Denis Fokin - Jumplist libraries changes. This is a release version of dll with eliminated MSVCRT dependencies.
7b0029d: 2013-08-26 Anton Makeev - CIDR: language tests in windows +review CR-OC @micha
9bef3a2: 2013-08-26 Konstantin Kolosovsky - IDEA-94942 Updated cleanup behavior after previous command failed
8447776: 2013-08-26 Mikhail Golubev - Remove SelectorBasedResponseHandler#getSelectorFileType, delegate to #getResponseType instead. Add missing @NotNull annotation, update doc comments.
d7ecb44: 2013-08-26 Anna Kozlova - treat classes with before/after methods as test classes for bad declared exceptions (IDEA-112537)
4be2bcf: 2013-08-26 Anna Kozlova - restore suggestion to remove 'abstract' when method has body
c57c308: 2013-08-26 Anna Kozlova - local can be final inside lambda body (IDEA-112630)
b8170a6: 2013-08-26 Vladimir Krivosheev - isValuesCustomSorted, add or not SortValuesAction (alphabetically sort) TODO: this action should be moved to "Variables" as gear action
b56b744: 2013-08-26 Mikhail Golubev - Merge remote-tracking branch 'origin/master'
ba7e194: 2013-08-23 Mikhail Golubev - Migrate Assembla to new GenericRepository
dd0885d: 2013-08-23 Mikhail Golubev - Refactor TemplateVariable
aa94a3c: 2013-08-23 Mikhail Golubev - Add several tests of date parsing
0e7e61e: 2013-08-23 Mikhail Golubev - Reflective PsiElements creation in JqlParserDefinition
a5f3011: 2013-08-26 Konstantin Kolosovsky - IDEA-94942 Short refactoring (removed duplication)
01cd38b: 2013-08-26 Sergey Simonchik - unnecessary line removed
7c34a2a: 2013-08-26 Dmitry Avdeev - IDEA-60895 No completion for enumerated and boolean values of xml tags
8b83a30: 2013-08-26 Vladislav.Soroka - gradle: cosmetic UI fix
8ee6a76: 2013-08-26 Roman Shevchenko - Convenient debug logging
cde5373: 2013-08-26 Dmitry Avdeev - IDEA-60895 No completion for enumerated and boolean values of xml tags
75d9b47: 2013-08-24 Max Medvedev - IDEA-112621 Groovy: Remove explicit type declaration intention
d1c29fb: 2013-08-23 Max Medvedev - separate visit methods for all types of classes, enums, interfaces, annotation types, and anonymous classes
5eca3d0: 2013-08-23 Max Medvedev - Byte code viewer shows byte code for groovy scripts
c96d27f: 2013-08-23 Max Medvedev - Convert anonymous class to closure: don't insert 'as Runnable' if groovy version is at least 2.2
7e993a0: 2013-08-23 Max Medvedev - IDEA-112560 process only applicable mixins to a ref
c33dc4a: 2013-08-26 Roman Shevchenko - platform: unified loading of system libraries (done right)
d492a6f: 2013-08-25 Roman Shevchenko - logging
2f2b546: 2013-08-25 Roman Shevchenko - IDEA-112462 (allow plugins to extend lib search path)
2a39bf2: 2013-08-25 Maxim.Mossienko - restart lexer from 0 offset when searching from start
a0858dbf: 2013-08-25 Maxim.Mossienko - 20% more compact compiler caches (-50M for IDEA project)
5ce3373: 2013-08-25 Maxim.Mossienko - IDEA-111918 Find: comments / string literals only: just 1 entry is found in each comment or literal
68ffc65: 2013-08-25 Maxim.Mossienko - faster contol + shift + N / control + N by default
eb67af1: 2013-08-24 Vassiliy Kudryashov - IDEA-107413 Cannot drag'n'drop more than one item in Changes View
d5ed7b5: 2013-08-24 nik - store properties of source folder in JPS element
b3dd357: 2013-08-24 nik - typo
5b8b1ff: 2013-08-24 Kirill Likhodedov - Merge remote-tracking branch 'origin/master'
0dd284a: 2013-08-24 Kirill Likhodedov - Better assertion error in the DefaultLogger
3146c0b: 2013-08-23 Bas Leijdekkers - foreach can also initialize field
6356a11: 2013-08-23 Konstantin Bulenkov - completely refacrored
6ad3452: 2013-08-23 Anna Kozlova - show conflict on invert boolean and method references (IDEA-112572)
4726276: 2013-08-23 Anna Kozlova - extract method from lambda body: accept parameters of parent method (IDEA-112570)
16a5e32: 2013-08-23 Gregory.Shrago - EditorEx: permanent header API
edc3497: 2013-08-23 Konstantin Kolosovsky - IDEA-94942 Content retrieval from svn refactored to ClientFactory model
0f40312: 2013-08-23 Mikhail Golubev - Change JqlQuery methods, JqlTerminalClause should extend JqlClause
c335f2b: 2013-08-23 Mikhail Golubev - IDEA-111811 Task management: JIRA: JQL: code completion suggests nothing after closing parenthesis
6e6972a: 2013-08-23 Konstantin Kolosovsky - IDEA-94942 Annotate action
e9d5412: 2013-08-23 Mikhail Golubev - Update description of NATIVE_SEARCH feature in TaskRepository
3c948a8: 2013-08-22 Konstantin Kolosovsky - IDEA-94942 Code cleanup - unused parameters removed
64706df: 2013-08-22 Konstantin Kolosovsky - IDEA-112184
0e41cad: 2013-08-22 Konstantin Kolosovsky - IDEA-94942 Simple property client to fix SvnMergeProvider.isBinary implementation
8091bb0: 2013-08-22 Konstantin Kolosovsky - IDEA-94942 Implemented file conflicts resolving Updated "svn info" result parsing
040f405: 2013-08-21 Konstantin Kolosovsky - IDEA-94942 SvnBindClient - unsupported methods removed
cef0440: 2013-08-21 Konstantin Kolosovsky - IDEA-94942 "bindSvn" module classes moved to "svn4idea" SvnBindClient unsupported methods will be removed in next commit (to track change like "rename" instead of "delete" + "add" to preserve history)
6c36a93: 2013-08-20 Konstantin Kolosovsky - IDEA-112184 Added logging to detect authentication issues
928c01e: 2013-08-07 Konstantin Kolosovsky - IDEA-94942 - Basic svn 1.8 test support
d880599: 2013-08-20 Konstantin Kolosovsky - IDEA-94942 Small text fixes Ignoring tests for old/not used functionality
71384c1: 2013-08-19 Konstantin Kolosovsky - IDEA-94942 Copy and move actions
a5f7e7c: 2013-08-19 Konstantin Kolosovsky - IDEA-94942 Delete action
e197c08: 2013-08-19 Konstantin Kolosovsky - IDEA-94942 Fixed authentication for svn protocol
c28c127: 2013-08-19 Konstantin Kolosovsky - IDEA-94942 Short add, revert commands refactoring
e06a346: 2013-08-19 Konstantin Kolosovsky - IDEA-94942 Revert action Fixed status command for single file Fixed status result parsing for normal (non-modified versioned) file
0f66f9d: 2013-08-19 Konstantin Kolosovsky - IDEA-94942 Logging and comments for some commands
c928fe5: 2013-08-16 Konstantin Kolosovsky - IDEA-94942 Force command line client usage if working copy of svn 1.8 format detected
d6e4e38: 2013-08-16 Konstantin Kolosovsky - IDEA-94942 Fixed "add" action output parsing for binary files
1236be5: 2013-08-15 Konstantin Kolosovsky - IDEA-94942 Small refactoring and fixes after review
e05b576: 2013-08-15 Konstantin Kolosovsky - IDEA-94942 "Add" action for files and directories
ccf6085: 2013-08-15 Konstantin Kolosovsky - IDEA-94942 "Subversion" -> "Show History" for files and folders
aba7390: 2013-08-14 Konstantin Kolosovsky - IDEA-94942 Add command refactored
555d597: 2013-08-14 Konstantin Kolosovsky - IDEA-94942 Fixing status, info commands to use correct arguments
b083f31: 2013-08-14 Konstantin Kolosovsky - IDEA-94942 Diff provider (without revision properties) Small command refactoring
f657dc8: 2013-08-13 Konstantin Kolosovsky - IDEA-94942 Authentication updates Authentication for remote status command Result parsing for remote info command 2 way SSL support (could be issues with ordinary password entering after 2 way SSL)
2785122: 2013-07-29 Konstantin Kolosovsky - IDEA-94942 initial svn 1.8 support with already existing command line functionality
857bfd5: 2013-08-12 max-kammerer - Update CoreJavaFileManagerTest.java
13539bc: 2013-08-09 Mikhael Bogdanov - CoreJavaFileManager.findClass: properly resolve $ in inner class names
Change-Id: Ica3d3d647183983bcd88ce2fb3450deb86343cdb
Diffstat (limited to 'plugins')
292 files changed, 5841 insertions, 3667 deletions
diff --git a/plugins/ByteCodeViewer/src/META-INF/plugin.xml b/plugins/ByteCodeViewer/src/META-INF/plugin.xml index 9a0135ae9121..e9c6e32b977d 100644 --- a/plugins/ByteCodeViewer/src/META-INF/plugin.xml +++ b/plugins/ByteCodeViewer/src/META-INF/plugin.xml @@ -5,6 +5,9 @@ <description>Viewing java bytecode inside IntelliJ IDEA.</description> <vendor>JetBrains</vendor> + <extensionPoints> + <extensionPoint name="classSearcher" interface="com.intellij.byteCodeViewer.ClassSearcher"/> + </extensionPoints> <extensions defaultExtensionNs="com.intellij"> <errorHandler implementation="com.intellij.diagnostic.ITNReporter"/> <projectService serviceInterface="com.intellij.byteCodeViewer.ByteCodeViewerManager" diff --git a/plugins/ByteCodeViewer/src/com/intellij/byteCodeViewer/ByteCodeViewerManager.java b/plugins/ByteCodeViewer/src/com/intellij/byteCodeViewer/ByteCodeViewerManager.java index a5fc96c7198b..7d958bb61b32 100644 --- a/plugins/ByteCodeViewer/src/com/intellij/byteCodeViewer/ByteCodeViewerManager.java +++ b/plugins/ByteCodeViewer/src/com/intellij/byteCodeViewer/ByteCodeViewerManager.java @@ -5,6 +5,7 @@ import com.intellij.ide.util.JavaAnonymousClassesHelper; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; @@ -35,6 +36,8 @@ import java.io.StringWriter; * Date: 5/7/12 */ public class ByteCodeViewerManager extends DockablePopupManager<ByteCodeViewerComponent> { + private static final ExtensionPointName<ClassSearcher> CLASS_SEARCHER_EP = ExtensionPointName.create("ByteCodeViewer.classSearcher"); + private static final Logger LOG = Logger.getInstance("#" + ByteCodeViewerManager.class.getName()); public static final String TOOLWINDOW_ID = "Byte Code Viewer"; @@ -232,10 +235,20 @@ public class ByteCodeViewerManager extends DockablePopupManager<ByteCodeViewerCo return ClassUtil.getJVMClassName(containingClass); } - private static PsiClass getContainingClass(PsiElement psiElement) { + public static PsiClass getContainingClass(PsiElement psiElement) { + for (ClassSearcher searcher : CLASS_SEARCHER_EP.getExtensions()) { + PsiClass aClass = searcher.findClass(psiElement); + if (aClass != null) { + return aClass; + } + } + return findClass(psiElement); + } + + public static PsiClass findClass(@NotNull PsiElement psiElement) { PsiClass containingClass = PsiTreeUtil.getParentOfType(psiElement, PsiClass.class, false); while (containingClass instanceof PsiTypeParameter) { - containingClass = PsiTreeUtil.getParentOfType(containingClass, PsiClass.class); + containingClass = PsiTreeUtil.getParentOfType(containingClass, PsiClass.class); } if (containingClass == null) return null; diff --git a/plugins/ByteCodeViewer/src/com/intellij/byteCodeViewer/ClassSearcher.java b/plugins/ByteCodeViewer/src/com/intellij/byteCodeViewer/ClassSearcher.java new file mode 100644 index 000000000000..9a778d1470c2 --- /dev/null +++ b/plugins/ByteCodeViewer/src/com/intellij/byteCodeViewer/ClassSearcher.java @@ -0,0 +1,14 @@ +package com.intellij.byteCodeViewer; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Created by Max Medvedev on 8/23/13 + */ +public interface ClassSearcher { + @Nullable + PsiClass findClass(@NotNull PsiElement place); +} diff --git a/plugins/ByteCodeViewer/src/com/intellij/byteCodeViewer/ShowByteCodeAction.java b/plugins/ByteCodeViewer/src/com/intellij/byteCodeViewer/ShowByteCodeAction.java index 34b5d446e3ba..76cf133e7a33 100644 --- a/plugins/ByteCodeViewer/src/com/intellij/byteCodeViewer/ShowByteCodeAction.java +++ b/plugins/ByteCodeViewer/src/com/intellij/byteCodeViewer/ShowByteCodeAction.java @@ -20,7 +20,6 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtilBase; import com.intellij.psi.util.PsiUtilCore; import com.intellij.ui.popup.NotLookupOrSearchCondition; @@ -43,7 +42,7 @@ public class ShowByteCodeAction extends AnAction { final PsiElement psiElement = getPsiElement(e.getDataContext(), project, e.getData(PlatformDataKeys.EDITOR)); if (psiElement != null) { if (psiElement.getContainingFile() instanceof PsiClassOwner && - PsiTreeUtil.getParentOfType(psiElement, PsiClass.class, false) != null) { + ByteCodeViewerManager.getContainingClass(psiElement) != null) { e.getPresentation().setEnabled(true); } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties index f62b033c85c8..8bb0e6803a02 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1228,6 +1228,9 @@ synchronized.method.ignore.synchronized.super.option=Ignore methods overriding a synchronized.method.move.quickfix=Move synchronization into method thread.run.replace.quickfix=Replace with 'start()' volatile.field.problem.descriptor=Volatile field <code>#ref</code> of type ''{0}'' #loc +string.format.choose.class=Choose Formatter class +string.format.class.column.name=Additional formatter classes +string.format.class.method.name=Additional formatter methods exception.class.column.name=Exception class bad.exception.thrown.problem.descriptor=Prohibited exception ''{0}'' thrown #loc empty.catch.block.comments.option=Comments count as content diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/InspectionGadgetsFix.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/InspectionGadgetsFix.java index 7cd60f3b9c56..d0feec6766b3 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/InspectionGadgetsFix.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/InspectionGadgetsFix.java @@ -29,7 +29,7 @@ import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.IncorrectOperationException; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/EmptyStatementBodyInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/EmptyStatementBodyInspection.java index 5e3b4c4d185c..afea6ecabdc6 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/EmptyStatementBodyInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/EmptyStatementBodyInspection.java @@ -21,7 +21,7 @@ import com.intellij.psi.*; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jdom.Element; import org.jetbrains.annotations.NotNull; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MalformedFormatStringInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MalformedFormatStringInspectionBase.java index 1cf19734a67e..052e7424ec31 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MalformedFormatStringInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/MalformedFormatStringInspectionBase.java @@ -15,6 +15,8 @@ */ package com.siyeh.ig.bugs; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; import com.intellij.psi.*; import com.intellij.psi.util.ConstantExpressionUtil; import com.intellij.psi.util.PsiUtil; @@ -23,9 +25,47 @@ import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.psiutils.ExpressionUtils; import com.siyeh.ig.psiutils.FormatUtils; +import org.jdom.Element; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -public class MalformedFormatStringInspection extends BaseInspection { +import java.util.ArrayList; +import java.util.List; + +public class MalformedFormatStringInspectionBase extends BaseInspection { + /** + * @noinspection PublicField + */ + @NonNls public String additionalClasses = ""; + + /** + * @noinspection PublicField + */ + @NonNls public String additionalMethods = ""; + + final List<String> classNames; + final List<String> methodNames; + + public MalformedFormatStringInspectionBase() { + classNames = new ArrayList<String>(); + methodNames = new ArrayList<String>(); + parseString(additionalClasses, classNames); + parseString(additionalMethods, methodNames); + } + + @Override + public void readSettings(@NotNull Element node) throws InvalidDataException { + super.readSettings(node); + parseString(additionalClasses, classNames); + parseString(additionalMethods, methodNames); + } + + @Override + public void writeSettings(@NotNull Element node) throws WriteExternalException { + additionalClasses = formatString(classNames); + additionalMethods = formatString(methodNames); + super.writeSettings(node); + } @Override @NotNull @@ -61,12 +101,12 @@ public class MalformedFormatStringInspection extends BaseInspection { return new MalformedFormatStringVisitor(); } - private static class MalformedFormatStringVisitor extends BaseInspectionVisitor { + private class MalformedFormatStringVisitor extends BaseInspectionVisitor { @Override public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) { super.visitMethodCallExpression(expression); - if (!FormatUtils.isFormatCall(expression)) { + if (!FormatUtils.isFormatCall(expression, methodNames, classNames)) { return; } final PsiExpressionList argumentList = expression.getArgumentList(); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/UnnecessaryContinueInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/UnnecessaryContinueInspection.java index 36a0dc906022..77a3ab33f26a 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/UnnecessaryContinueInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/UnnecessaryContinueInspection.java @@ -24,7 +24,7 @@ import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.fixes.DeleteUnnecessaryStatementFix; import com.siyeh.ig.psiutils.ControlFlowUtils; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jetbrains.annotations.NotNull; import javax.swing.*; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/UnnecessaryReturnInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/UnnecessaryReturnInspection.java index 5c8f1df3857b..48c10184cb54 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/UnnecessaryReturnInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/UnnecessaryReturnInspection.java @@ -24,7 +24,7 @@ import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.fixes.DeleteUnnecessaryStatementFix; import com.siyeh.ig.psiutils.ControlFlowUtils; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jetbrains.annotations.NotNull; import javax.swing.*; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/dataflow/TooBroadScopeInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/dataflow/TooBroadScopeInspectionBase.java index 07b77868fcce..435dd24cb0cd 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/dataflow/TooBroadScopeInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/dataflow/TooBroadScopeInspectionBase.java @@ -27,7 +27,7 @@ import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.psiutils.ClassUtils; import com.siyeh.ig.psiutils.ExpressionUtils; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import com.siyeh.ig.psiutils.VariableAccessUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/EmptyCatchBlockInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/EmptyCatchBlockInspection.java index 4a9b58bd75aa..fa50fb44cc21 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/EmptyCatchBlockInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/EmptyCatchBlockInspection.java @@ -24,7 +24,7 @@ import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import com.siyeh.ig.psiutils.TestUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/EmptyFinallyBlockInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/EmptyFinallyBlockInspection.java index 8e3fbeb5c4c9..741d7cfb8b4e 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/EmptyFinallyBlockInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/EmptyFinallyBlockInspection.java @@ -25,7 +25,7 @@ import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jetbrains.annotations.NotNull; public class EmptyFinallyBlockInspection extends BaseInspection { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/EmptyTryBlockInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/EmptyTryBlockInspection.java index 57003f5f4a0a..04f50afdaa2d 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/EmptyTryBlockInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/errorhandling/EmptyTryBlockInspection.java @@ -20,7 +20,7 @@ import com.intellij.psi.PsiTryStatement; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jetbrains.annotations.NotNull; public class EmptyTryBlockInspection extends BaseInspection { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/JavaLangImportInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/JavaLangImportInspection.java index 36c31ac74792..f0d521d3ed9c 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/JavaLangImportInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/JavaLangImportInspection.java @@ -22,7 +22,7 @@ import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.fixes.DeleteImportFix; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import com.siyeh.ig.psiutils.ImportUtils; import org.jetbrains.annotations.NotNull; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/OnDemandImportInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/OnDemandImportInspection.java index d9d9dd9445c2..f9d5aea7344d 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/OnDemandImportInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/OnDemandImportInspection.java @@ -19,7 +19,7 @@ import com.intellij.psi.*; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jetbrains.annotations.NotNull; public class OnDemandImportInspection extends BaseInspection { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/SamePackageImportInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/SamePackageImportInspection.java index 579a20bd8861..3d9ee1148ab9 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/SamePackageImportInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/SamePackageImportInspection.java @@ -21,7 +21,7 @@ import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.fixes.DeleteImportFix; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jetbrains.annotations.NotNull; public class SamePackageImportInspection extends BaseInspection { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/SingleClassImportInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/SingleClassImportInspection.java index 91692ab21eb2..9bcad8fc0a95 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/SingleClassImportInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/SingleClassImportInspection.java @@ -22,7 +22,7 @@ import com.intellij.psi.PsiJavaFile; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jetbrains.annotations.NotNull; public class SingleClassImportInspection extends BaseInspection { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/UnusedImportInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/UnusedImportInspection.java index 49c58d15ba56..7b6b72a2e2e8 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/UnusedImportInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/imports/UnusedImportInspection.java @@ -21,7 +21,7 @@ import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.fixes.DeleteImportFix; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/PrivateMemberAccessBetweenOuterAndInnerClassInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/PrivateMemberAccessBetweenOuterAndInnerClassInspection.java index bc1fbe0bec87..a1952f0c747e 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/PrivateMemberAccessBetweenOuterAndInnerClassInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/PrivateMemberAccessBetweenOuterAndInnerClassInspection.java @@ -25,7 +25,7 @@ import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.psiutils.ClassUtils; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javabeans/ClassWithoutConstructorInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javabeans/ClassWithoutConstructorInspection.java index 2aa3c07bf0f1..d1bd35e9e508 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javabeans/ClassWithoutConstructorInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javabeans/ClassWithoutConstructorInspection.java @@ -24,7 +24,7 @@ import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jetbrains.annotations.NotNull; public class ClassWithoutConstructorInspection extends BaseInspection { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FileTypeUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FileTypeUtils.java deleted file mode 100644 index 788812176db3..000000000000 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FileTypeUtils.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.siyeh.ig.psiutils; - -import com.intellij.psi.PsiElement; -import com.intellij.psi.ServerPageFile; -import com.intellij.psi.util.PsiUtilCore; - -public class FileTypeUtils { - public static boolean isInServerPageFile(PsiElement file) { - return PsiUtilCore.getTemplateLanguageFile(file) instanceof ServerPageFile; - } -} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FormatUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FormatUtils.java index 68e431e5652f..9be61bc1302d 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FormatUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/FormatUtils.java @@ -20,7 +20,9 @@ import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; +import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Set; public class FormatUtils { @@ -48,9 +50,13 @@ public class FormatUtils { private FormatUtils() {} public static boolean isFormatCall(PsiMethodCallExpression expression) { + return isFormatCall(expression, Collections.<String>emptyList(), Collections.<String>emptyList()); + } + + public static boolean isFormatCall(PsiMethodCallExpression expression, List<String> optionalMethods, List<String> optionalClasses) { final PsiReferenceExpression methodExpression = expression.getMethodExpression(); final String name = methodExpression.getReferenceName(); - if (!formatMethodNames.contains(name)) { + if (!formatMethodNames.contains(name) && !optionalMethods.contains(name)) { return false; } final PsiMethod method = expression.resolveMethod(); @@ -62,7 +68,7 @@ public class FormatUtils { return false; } final String className = containingClass.getQualifiedName(); - return formatClassNames.contains(className); + return formatClassNames.contains(className) || optionalClasses.contains(className); } public static boolean isFormatCallArgument(PsiElement element) { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/InitializationUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/InitializationUtils.java index 29c8b46b8b44..67c6a32a60f1 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/InitializationUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/InitializationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2013 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,58 +21,43 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.HashSet; -import java.util.List; import java.util.Set; public class InitializationUtils { private InitializationUtils() {} - public static boolean methodAssignsVariableOrFails( - @Nullable PsiMethod method, @NotNull PsiVariable variable) { + public static boolean methodAssignsVariableOrFails(@Nullable PsiMethod method, @NotNull PsiVariable variable) { return methodAssignsVariableOrFails(method, variable, false); } - public static boolean expressionAssignsVariableOrFails( - @Nullable PsiExpression expression, @NotNull PsiVariable variable) { - return expressionAssignsVariableOrFails(expression, variable, - new HashSet(), true); + public static boolean expressionAssignsVariableOrFails(@Nullable PsiExpression expression, @NotNull PsiVariable variable) { + return expressionAssignsVariableOrFails(expression, variable, new HashSet(), true); } - public static boolean methodAssignsVariableOrFails( - @Nullable PsiMethod method, @NotNull PsiVariable variable, - boolean strict) { + public static boolean methodAssignsVariableOrFails(@Nullable PsiMethod method, @NotNull PsiVariable variable, boolean strict) { if (method == null) { return false; } - final PsiCodeBlock body = method.getBody(); - return body != null && blockAssignsVariableOrFails(body, variable, - strict); + return blockAssignsVariableOrFails(method.getBody(), variable, strict); } - public static boolean blockAssignsVariableOrFails( - @Nullable PsiCodeBlock block, @NotNull PsiVariable variable) { + public static boolean blockAssignsVariableOrFails(@Nullable PsiCodeBlock block, @NotNull PsiVariable variable) { return blockAssignsVariableOrFails(block, variable, false); } - public static boolean blockAssignsVariableOrFails( - @Nullable PsiCodeBlock block, @NotNull PsiVariable variable, - boolean strict) { - return blockAssignsVariableOrFails(block, variable, - new HashSet<MethodSignature>(), strict); + public static boolean blockAssignsVariableOrFails(@Nullable PsiCodeBlock block, @NotNull PsiVariable variable, boolean strict) { + return blockAssignsVariableOrFails(block, variable, new HashSet<MethodSignature>(), strict); } - private static boolean blockAssignsVariableOrFails( - @Nullable PsiCodeBlock block, @NotNull PsiVariable variable, - @NotNull Set<MethodSignature> checkedMethods, boolean strict) { + private static boolean blockAssignsVariableOrFails(@Nullable PsiCodeBlock block, @NotNull PsiVariable variable, + @NotNull Set<MethodSignature> checkedMethods, boolean strict) { if (block == null) { return false; } - final PsiStatement[] statements = block.getStatements(); int assignmentCount = 0; - for (final PsiStatement statement : statements) { - if (statementAssignsVariableOrFails(statement, variable, - checkedMethods, strict)) { + for (final PsiStatement statement : block.getStatements()) { + if (statementAssignsVariableOrFails(statement, variable, checkedMethods, strict)) { if (strict) { assignmentCount++; } @@ -84,9 +69,8 @@ public class InitializationUtils { return assignmentCount == 1; } - private static boolean statementAssignsVariableOrFails( - @Nullable PsiStatement statement, PsiVariable variable, - @NotNull Set<MethodSignature> checkedMethods, boolean strict) { + private static boolean statementAssignsVariableOrFails(@Nullable PsiStatement statement, PsiVariable variable, + @NotNull Set<MethodSignature> checkedMethods, boolean strict) { if (statement == null) { return false; } @@ -101,107 +85,70 @@ public class InitializationUtils { return false; } else if (statement instanceof PsiReturnStatement) { - final PsiReturnStatement returnStatement = - (PsiReturnStatement)statement; - final PsiExpression returnValue = returnStatement.getReturnValue(); - return expressionAssignsVariableOrFails(returnValue, variable, - checkedMethods, strict); + final PsiReturnStatement returnStatement = (PsiReturnStatement)statement; + return expressionAssignsVariableOrFails(returnStatement.getReturnValue(), variable, checkedMethods, strict); } else if (statement instanceof PsiThrowStatement) { - final PsiThrowStatement throwStatement = - (PsiThrowStatement)statement; - final PsiExpression exception = throwStatement.getException(); - return expressionAssignsVariableOrFails(exception, variable, - checkedMethods, strict); + final PsiThrowStatement throwStatement = (PsiThrowStatement)statement; + return expressionAssignsVariableOrFails(throwStatement.getException(), variable, checkedMethods, strict); } else if (statement instanceof PsiExpressionListStatement) { - final PsiExpressionListStatement list = - (PsiExpressionListStatement)statement; + final PsiExpressionListStatement list = (PsiExpressionListStatement)statement; final PsiExpressionList expressionList = list.getExpressionList(); - final PsiExpression[] expressions = expressionList.getExpressions(); - for (final PsiExpression expression : expressions) { - if (expressionAssignsVariableOrFails(expression, variable, - checkedMethods, strict)) { + for (final PsiExpression expression : expressionList.getExpressions()) { + if (expressionAssignsVariableOrFails(expression, variable, checkedMethods, strict)) { return true; } } return false; } else if (statement instanceof PsiExpressionStatement) { - final PsiExpressionStatement expressionStatement = - (PsiExpressionStatement)statement; - final PsiExpression expression = - expressionStatement.getExpression(); - return expressionAssignsVariableOrFails(expression, variable, - checkedMethods, strict); + final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)statement; + return expressionAssignsVariableOrFails(expressionStatement.getExpression(), variable, checkedMethods, strict); } else if (statement instanceof PsiDeclarationStatement) { - final PsiDeclarationStatement declarationStatement = - (PsiDeclarationStatement)statement; - return declarationStatementAssignsVariableOrFails( - declarationStatement, variable, checkedMethods, strict); + final PsiDeclarationStatement declarationStatement = (PsiDeclarationStatement)statement; + return declarationStatementAssignsVariableOrFails(declarationStatement, variable, checkedMethods, strict); } else if (statement instanceof PsiForStatement) { final PsiForStatement forStatement = (PsiForStatement)statement; - return forStatementAssignsVariableOrFails(forStatement, - variable, - checkedMethods, strict); + return forStatementAssignsVariableOrFails(forStatement, variable, checkedMethods, strict); } else if (statement instanceof PsiForeachStatement) { - final PsiForeachStatement foreachStatement = - (PsiForeachStatement)statement; - return foreachStatementAssignsVariableOrFails(variable, - foreachStatement); + final PsiForeachStatement foreachStatement = (PsiForeachStatement)statement; + return foreachStatementAssignsVariableOrFails(foreachStatement, variable, checkedMethods, strict); } else if (statement instanceof PsiWhileStatement) { - final PsiWhileStatement whileStatement = - (PsiWhileStatement)statement; - return whileStatementAssignsVariableOrFails(whileStatement, - variable, checkedMethods, strict); + final PsiWhileStatement whileStatement = (PsiWhileStatement)statement; + return whileStatementAssignsVariableOrFails(whileStatement, variable, checkedMethods, strict); } else if (statement instanceof PsiDoWhileStatement) { - final PsiDoWhileStatement doWhileStatement = - (PsiDoWhileStatement)statement; - return doWhileAssignsVariableOrFails(doWhileStatement, variable, - checkedMethods, strict); + final PsiDoWhileStatement doWhileStatement = (PsiDoWhileStatement)statement; + return doWhileAssignsVariableOrFails(doWhileStatement, variable, checkedMethods, strict); } else if (statement instanceof PsiSynchronizedStatement) { - final PsiSynchronizedStatement synchronizedStatement = - (PsiSynchronizedStatement)statement; - final PsiCodeBlock body = synchronizedStatement.getBody(); - return blockAssignsVariableOrFails(body, variable, - checkedMethods, strict); + final PsiSynchronizedStatement synchronizedStatement = (PsiSynchronizedStatement)statement; + return blockAssignsVariableOrFails(synchronizedStatement.getBody(), variable, checkedMethods, strict); } else if (statement instanceof PsiBlockStatement) { - final PsiBlockStatement blockStatement = - (PsiBlockStatement)statement; - final PsiCodeBlock codeBlock = blockStatement.getCodeBlock(); - return blockAssignsVariableOrFails(codeBlock, variable, - checkedMethods, strict); + final PsiBlockStatement blockStatement = (PsiBlockStatement)statement; + return blockAssignsVariableOrFails(blockStatement.getCodeBlock(), variable, checkedMethods, strict); } else if (statement instanceof PsiLabeledStatement) { - final PsiLabeledStatement labeledStatement = - (PsiLabeledStatement)statement; - final PsiStatement statementLabeled = - labeledStatement.getStatement(); - return statementAssignsVariableOrFails(statementLabeled, variable, - checkedMethods, strict); + final PsiLabeledStatement labeledStatement = (PsiLabeledStatement)statement; + return statementAssignsVariableOrFails(labeledStatement.getStatement(), variable, checkedMethods, strict); } else if (statement instanceof PsiIfStatement) { final PsiIfStatement ifStatement = (PsiIfStatement)statement; - return ifStatementAssignsVariableOrFails(ifStatement, variable, - checkedMethods, strict); + return ifStatementAssignsVariableOrFails(ifStatement, variable, checkedMethods, strict); } else if (statement instanceof PsiTryStatement) { final PsiTryStatement tryStatement = (PsiTryStatement)statement; - return tryStatementAssignsVariableOrFails(tryStatement, variable, - checkedMethods, strict); + return tryStatementAssignsVariableOrFails(tryStatement, variable, checkedMethods, strict); } else if (statement instanceof PsiSwitchStatement) { - final PsiSwitchStatement switchStatement = - (PsiSwitchStatement)statement; - return switchStatementAssignsVariableOrFails(switchStatement, - variable, checkedMethods, strict); + final PsiSwitchStatement switchStatement = (PsiSwitchStatement)statement; + return switchStatementAssignsVariableOrFails(switchStatement, variable, checkedMethods, strict); } else { // unknown statement type @@ -209,21 +156,15 @@ public class InitializationUtils { } } - public static boolean switchStatementAssignsVariableOrFails( - @NotNull PsiSwitchStatement switchStatement, - @NotNull PsiVariable variable, - boolean strict) { - return switchStatementAssignsVariableOrFails(switchStatement, variable, - new HashSet(), strict); + public static boolean switchStatementAssignsVariableOrFails(@NotNull PsiSwitchStatement switchStatement, @NotNull PsiVariable variable, + boolean strict) { + return switchStatementAssignsVariableOrFails(switchStatement, variable, new HashSet(), strict); } - private static boolean switchStatementAssignsVariableOrFails( - @NotNull PsiSwitchStatement switchStatement, - @NotNull PsiVariable variable, - @NotNull Set<MethodSignature> checkedMethods, boolean strict) { + private static boolean switchStatementAssignsVariableOrFails(@NotNull PsiSwitchStatement switchStatement, @NotNull PsiVariable variable, + @NotNull Set<MethodSignature> checkedMethods, boolean strict) { final PsiExpression expression = switchStatement.getExpression(); - if (expressionAssignsVariableOrFails(expression, variable, - checkedMethods, strict)) { + if (expressionAssignsVariableOrFails(expression, variable, checkedMethods, strict)) { return true; } final PsiCodeBlock body = switchStatement.getBody(); @@ -236,8 +177,7 @@ public class InitializationUtils { for (int i = 0; i < statements.length; i++) { final PsiStatement statement = statements[i]; if (statement instanceof PsiSwitchLabelStatement) { - final PsiSwitchLabelStatement labelStatement - = (PsiSwitchLabelStatement)statement; + final PsiSwitchLabelStatement labelStatement = (PsiSwitchLabelStatement)statement; if (i == statements.length - 1) { return false; } @@ -247,8 +187,7 @@ public class InitializationUtils { assigns = false; } else if (statement instanceof PsiBreakStatement) { - final PsiBreakStatement breakStatement - = (PsiBreakStatement)statement; + final PsiBreakStatement breakStatement = (PsiBreakStatement)statement; if (breakStatement.getLabelIdentifier() != null) { return false; } @@ -258,8 +197,7 @@ public class InitializationUtils { assigns = false; } else { - assigns |= statementAssignsVariableOrFails(statement, variable, - checkedMethods, strict); + assigns |= statementAssignsVariableOrFails(statement, variable, checkedMethods, strict); if (i == statements.length - 1 && !assigns) { return false; } @@ -268,18 +206,13 @@ public class InitializationUtils { return containsDefault; } - private static boolean declarationStatementAssignsVariableOrFails( - PsiDeclarationStatement declarationStatement, PsiVariable variable, - Set<MethodSignature> checkedMethods, boolean strict) { - final PsiElement[] elements = - declarationStatement.getDeclaredElements(); + private static boolean declarationStatementAssignsVariableOrFails(PsiDeclarationStatement declarationStatement, PsiVariable variable, + Set<MethodSignature> checkedMethods, boolean strict) { + final PsiElement[] elements = declarationStatement.getDeclaredElements(); for (PsiElement element : elements) { if (element instanceof PsiVariable) { final PsiVariable declaredVariable = (PsiVariable)element; - final PsiExpression initializer = - declaredVariable.getInitializer(); - if (expressionAssignsVariableOrFails(initializer, variable, - checkedMethods, strict)) { + if (expressionAssignsVariableOrFails(declaredVariable.getInitializer(), variable, checkedMethods, strict)) { return true; } } @@ -291,16 +224,14 @@ public class InitializationUtils { @NotNull Set<MethodSignature> checkedMethods, boolean strict) { final PsiResourceList resourceList = tryStatement.getResourceList(); if (resourceList != null) { - final List<PsiResourceVariable> resourceVariables = resourceList.getResourceVariables(); - for (PsiResourceVariable resourceVariable : resourceVariables) { + for (PsiResourceVariable resourceVariable : resourceList.getResourceVariables()) { final PsiExpression initializer = resourceVariable.getInitializer(); if (expressionAssignsVariableOrFails(initializer, variable, checkedMethods, strict)) { return true; } } } - final PsiCodeBlock tryBlock = tryStatement.getTryBlock(); - boolean initializedInTryAndCatch = blockAssignsVariableOrFails(tryBlock, variable, checkedMethods, strict); + boolean initializedInTryAndCatch = blockAssignsVariableOrFails(tryStatement.getTryBlock(), variable, checkedMethods, strict); final PsiCodeBlock[] catchBlocks = tryStatement.getCatchBlocks(); for (final PsiCodeBlock catchBlock : catchBlocks) { if (strict) { @@ -310,101 +241,71 @@ public class InitializationUtils { initializedInTryAndCatch &= blockAssignsVariableOrFails(catchBlock, variable, checkedMethods, strict); } } - if (initializedInTryAndCatch) { - return true; - } - final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock(); - return blockAssignsVariableOrFails(finallyBlock, variable, checkedMethods, strict); + return initializedInTryAndCatch || blockAssignsVariableOrFails(tryStatement.getFinallyBlock(), variable, checkedMethods, strict); } - private static boolean ifStatementAssignsVariableOrFails( - @NotNull PsiIfStatement ifStatement, - PsiVariable variable, - @NotNull Set<MethodSignature> checkedMethods, - boolean strict) { + private static boolean ifStatementAssignsVariableOrFails(@NotNull PsiIfStatement ifStatement, PsiVariable variable, + @NotNull Set<MethodSignature> checkedMethods, boolean strict) { final PsiExpression condition = ifStatement.getCondition(); - if (expressionAssignsVariableOrFails(condition, variable, - checkedMethods, strict)) { + if (expressionAssignsVariableOrFails(condition, variable, checkedMethods, strict)) { return true; } final PsiStatement thenBranch = ifStatement.getThenBranch(); - final PsiStatement elseBranch = ifStatement.getElseBranch(); if (BoolUtils.isTrue(condition)) { - return statementAssignsVariableOrFails(thenBranch, variable, - checkedMethods, strict); + return statementAssignsVariableOrFails(thenBranch, variable, checkedMethods, strict); } - else if (BoolUtils.isFalse(condition)) { - return statementAssignsVariableOrFails(elseBranch, variable, - checkedMethods, strict); + final PsiStatement elseBranch = ifStatement.getElseBranch(); + if (BoolUtils.isFalse(condition)) { + return statementAssignsVariableOrFails(elseBranch, variable, checkedMethods, strict); } - return statementAssignsVariableOrFails(thenBranch, variable, - checkedMethods, strict) && - statementAssignsVariableOrFails(elseBranch, variable, - checkedMethods, strict); + return statementAssignsVariableOrFails(thenBranch, variable, checkedMethods, strict) && + statementAssignsVariableOrFails(elseBranch, variable, checkedMethods, strict); } - private static boolean doWhileAssignsVariableOrFails( - @NotNull PsiDoWhileStatement doWhileStatement, - PsiVariable variable, - @NotNull Set<MethodSignature> checkedMethods, - boolean strict) { - final PsiExpression condition = doWhileStatement.getCondition(); - final PsiStatement body = doWhileStatement.getBody(); - return expressionAssignsVariableOrFails(condition, variable, - checkedMethods, strict) || - statementAssignsVariableOrFails(body, variable, checkedMethods, - strict); + private static boolean doWhileAssignsVariableOrFails(@NotNull PsiDoWhileStatement doWhileStatement, PsiVariable variable, + @NotNull Set<MethodSignature> checkedMethods, boolean strict) { + return statementAssignsVariableOrFails(doWhileStatement.getBody(), variable, checkedMethods, strict) || + expressionAssignsVariableOrFails(doWhileStatement.getCondition(), variable, checkedMethods, strict); } - private static boolean whileStatementAssignsVariableOrFails( - @NotNull PsiWhileStatement whileStatement, PsiVariable variable, - @NotNull Set<MethodSignature> checkedMethods, - boolean strict) { + private static boolean whileStatementAssignsVariableOrFails(@NotNull PsiWhileStatement whileStatement, PsiVariable variable, + @NotNull Set<MethodSignature> checkedMethods, boolean strict) { final PsiExpression condition = whileStatement.getCondition(); - if (expressionAssignsVariableOrFails(condition, variable, - checkedMethods, strict)) { + if (expressionAssignsVariableOrFails(condition, variable, checkedMethods, strict)) { return true; } if (BoolUtils.isTrue(condition)) { final PsiStatement body = whileStatement.getBody(); - if (statementAssignsVariableOrFails(body, variable, checkedMethods, - strict)) { + if (statementAssignsVariableOrFails(body, variable, checkedMethods, strict)) { return true; } } return false; } - private static boolean forStatementAssignsVariableOrFails( - @NotNull PsiForStatement forStatement, PsiVariable variable, - @NotNull Set<MethodSignature> checkedMethods, boolean strict) { - final PsiStatement initialization = forStatement.getInitialization(); - if (statementAssignsVariableOrFails(initialization, variable, - checkedMethods, strict)) { + private static boolean forStatementAssignsVariableOrFails(@NotNull PsiForStatement forStatement, PsiVariable variable, + @NotNull Set<MethodSignature> checkedMethods, boolean strict) { + if (statementAssignsVariableOrFails(forStatement.getInitialization(), variable, checkedMethods, strict)) { return true; } - final PsiExpression test = forStatement.getCondition(); - if (expressionAssignsVariableOrFails(test, variable, checkedMethods, - strict)) { + final PsiExpression condition = forStatement.getCondition(); + if (expressionAssignsVariableOrFails(condition, variable, checkedMethods, strict)) { return true; } - if (BoolUtils.isTrue(test)) { - final PsiStatement body = forStatement.getBody(); - if (statementAssignsVariableOrFails(body, variable, checkedMethods, - strict)) { + if (BoolUtils.isTrue(condition)) { + if (statementAssignsVariableOrFails(forStatement.getBody(), variable, checkedMethods, strict)) { return true; } - final PsiStatement update = forStatement.getUpdate(); - if (statementAssignsVariableOrFails(update, variable, - checkedMethods, strict)) { + if (statementAssignsVariableOrFails(forStatement.getUpdate(), variable, checkedMethods, strict)) { return true; } } return false; } - private static boolean foreachStatementAssignsVariableOrFails(PsiVariable field, PsiForeachStatement forStatement) { - return false; + private static boolean foreachStatementAssignsVariableOrFails(@NotNull PsiForeachStatement foreachStatement, PsiVariable field, + @NotNull Set<MethodSignature> checkedMethods, boolean strict) { + return expressionAssignsVariableOrFails(foreachStatement.getIteratedValue(), field, checkedMethods, strict); } private static boolean expressionAssignsVariableOrFails(@Nullable PsiExpression expression, PsiVariable variable, @@ -421,8 +322,7 @@ public class InitializationUtils { } else if (expression instanceof PsiParenthesizedExpression) { final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)expression; - final PsiExpression unparenthesizedExpression = parenthesizedExpression.getExpression(); - return expressionAssignsVariableOrFails(unparenthesizedExpression, variable, checkedMethods, strict); + return expressionAssignsVariableOrFails(parenthesizedExpression.getExpression(), variable, checkedMethods, strict); } else if (expression instanceof PsiMethodCallExpression) { final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression; @@ -434,8 +334,7 @@ public class InitializationUtils { } else if (expression instanceof PsiArrayInitializerExpression) { final PsiArrayInitializerExpression array = (PsiArrayInitializerExpression)expression; - final PsiExpression[] initializers = array.getInitializers(); - for (final PsiExpression initializer : initializers) { + for (final PsiExpression initializer : array.getInitializers()) { if (expressionAssignsVariableOrFails(initializer, variable, checkedMethods, strict)) { return true; } @@ -444,30 +343,24 @@ public class InitializationUtils { } else if (expression instanceof PsiTypeCastExpression) { final PsiTypeCastExpression typeCast = (PsiTypeCastExpression)expression; - final PsiExpression operand = typeCast.getOperand(); - return expressionAssignsVariableOrFails(operand, variable, checkedMethods, strict); + return expressionAssignsVariableOrFails(typeCast.getOperand(), variable, checkedMethods, strict); } else if (expression instanceof PsiArrayAccessExpression) { final PsiArrayAccessExpression accessExpression = (PsiArrayAccessExpression)expression; - final PsiExpression arrayExpression = accessExpression.getArrayExpression(); - final PsiExpression indexExpression = accessExpression.getIndexExpression(); - return expressionAssignsVariableOrFails(arrayExpression, variable, checkedMethods, strict) || - expressionAssignsVariableOrFails(indexExpression, variable, checkedMethods, strict); + return expressionAssignsVariableOrFails(accessExpression.getArrayExpression(), variable, checkedMethods, strict) || + expressionAssignsVariableOrFails(accessExpression.getIndexExpression(), variable, checkedMethods, strict); } else if (expression instanceof PsiPrefixExpression) { final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)expression; - final PsiExpression operand = prefixExpression.getOperand(); - return expressionAssignsVariableOrFails(operand, variable, checkedMethods, strict); + return expressionAssignsVariableOrFails(prefixExpression.getOperand(), variable, checkedMethods, strict); } else if (expression instanceof PsiPostfixExpression) { final PsiPostfixExpression postfixExpression = (PsiPostfixExpression)expression; - final PsiExpression operand = postfixExpression.getOperand(); - return expressionAssignsVariableOrFails(operand, variable, checkedMethods, strict); + return expressionAssignsVariableOrFails(postfixExpression.getOperand(), variable, checkedMethods, strict); } else if (expression instanceof PsiPolyadicExpression) { final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression; - final PsiExpression[] operands = polyadicExpression.getOperands(); - for (PsiExpression operand : operands) { + for (PsiExpression operand : polyadicExpression.getOperands()) { if (expressionAssignsVariableOrFails(operand, variable, checkedMethods, strict)) { return true; } @@ -476,14 +369,11 @@ public class InitializationUtils { } else if (expression instanceof PsiConditionalExpression) { final PsiConditionalExpression conditional = (PsiConditionalExpression)expression; - final PsiExpression condition = conditional.getCondition(); - if (expressionAssignsVariableOrFails(condition, variable, checkedMethods, strict)) { + if (expressionAssignsVariableOrFails(conditional.getCondition(), variable, checkedMethods, strict)) { return true; } - final PsiExpression thenExpression = conditional.getThenExpression(); - final PsiExpression elseExpression = conditional.getElseExpression(); - return expressionAssignsVariableOrFails(thenExpression, variable, checkedMethods, strict) && - expressionAssignsVariableOrFails(elseExpression, variable, checkedMethods, strict); + return expressionAssignsVariableOrFails(conditional.getThenExpression(), variable, checkedMethods, strict) && + expressionAssignsVariableOrFails(conditional.getElseExpression(), variable, checkedMethods, strict); } else if (expression instanceof PsiAssignmentExpression) { final PsiAssignmentExpression assignment = (PsiAssignmentExpression)expression; @@ -491,13 +381,12 @@ public class InitializationUtils { if (expressionAssignsVariableOrFails(lhs, variable, checkedMethods, strict)) { return true; } - final PsiExpression rhs = assignment.getRExpression(); - if (expressionAssignsVariableOrFails(rhs, variable, checkedMethods, strict)) { + if (expressionAssignsVariableOrFails(assignment.getRExpression(), variable, checkedMethods, strict)) { return true; } if (lhs instanceof PsiReferenceExpression) { final PsiElement element = ((PsiReference)lhs).resolve(); - if (element != null && element.equals(variable)) { + if (variable.equals(element)) { return true; } } @@ -505,85 +394,64 @@ public class InitializationUtils { } else if (expression instanceof PsiInstanceOfExpression) { final PsiInstanceOfExpression instanceOfExpression = (PsiInstanceOfExpression)expression; - final PsiExpression operand = instanceOfExpression.getOperand(); - return expressionAssignsVariableOrFails(operand, variable, checkedMethods, strict); + return expressionAssignsVariableOrFails(instanceOfExpression.getOperand(), variable, checkedMethods, strict); } else { return false; } } - private static boolean newExpressionAssignsVariableOrFails( - @NotNull PsiNewExpression newExpression, PsiVariable variable, - @NotNull Set<MethodSignature> checkedMethods, boolean strict) { + private static boolean newExpressionAssignsVariableOrFails(@NotNull PsiNewExpression newExpression, PsiVariable variable, + @NotNull Set<MethodSignature> checkedMethods, boolean strict) { final PsiExpressionList argumentList = newExpression.getArgumentList(); if (argumentList != null) { - final PsiExpression[] args = argumentList.getExpressions(); - for (final PsiExpression arg : args) { - if (expressionAssignsVariableOrFails(arg, variable, - checkedMethods, strict)) { + for (final PsiExpression argument : argumentList.getExpressions()) { + if (expressionAssignsVariableOrFails(argument, variable, checkedMethods, strict)) { return true; } } } - final PsiArrayInitializerExpression arrayInitializer = - newExpression.getArrayInitializer(); - if (expressionAssignsVariableOrFails(arrayInitializer, variable, - checkedMethods, strict)) { + if (expressionAssignsVariableOrFails(newExpression.getArrayInitializer(), variable, checkedMethods, strict)) { return true; } - final PsiExpression[] arrayDimensions = - newExpression.getArrayDimensions(); - for (final PsiExpression dim : arrayDimensions) { - if (expressionAssignsVariableOrFails(dim, variable, - checkedMethods, strict)) { + for (final PsiExpression dimension : newExpression.getArrayDimensions()) { + if (expressionAssignsVariableOrFails(dimension, variable, checkedMethods, strict)) { return true; } } return false; } - private static boolean methodCallAssignsVariableOrFails( - @NotNull PsiMethodCallExpression callExpression, - PsiVariable variable, - @NotNull Set<MethodSignature> checkedMethods, boolean strict) { - final PsiExpressionList argList = callExpression.getArgumentList(); - final PsiExpression[] args = argList.getExpressions(); - for (final PsiExpression arg : args) { - if (expressionAssignsVariableOrFails(arg, variable, checkedMethods, - strict)) { + private static boolean methodCallAssignsVariableOrFails(@NotNull PsiMethodCallExpression callExpression, PsiVariable variable, + @NotNull Set<MethodSignature> checkedMethods, boolean strict) { + final PsiExpressionList argumentList = callExpression.getArgumentList(); + for (final PsiExpression argument : argumentList.getExpressions()) { + if (expressionAssignsVariableOrFails(argument, variable, checkedMethods, strict)) { return true; } } - final PsiReferenceExpression methodExpression = - callExpression.getMethodExpression(); - if (expressionAssignsVariableOrFails(methodExpression, variable, - checkedMethods, strict)) { + if (expressionAssignsVariableOrFails(callExpression.getMethodExpression(), variable, checkedMethods, strict)) { return true; } final PsiMethod method = callExpression.resolveMethod(); if (method == null) { return false; } - final MethodSignature methodSignature = - method.getSignature(PsiSubstitutor.EMPTY); + final MethodSignature methodSignature = method.getSignature(PsiSubstitutor.EMPTY); if (!checkedMethods.add(methodSignature)) { return false; } - final PsiClass containingClass = - ClassUtils.getContainingClass(callExpression); + final PsiClass containingClass = ClassUtils.getContainingClass(callExpression); final PsiClass calledClass = method.getContainingClass(); if (calledClass == null || !calledClass.equals(containingClass)) { return false; } if (method.hasModifierProperty(PsiModifier.STATIC) - || method.isConstructor() || method.hasModifierProperty(PsiModifier.PRIVATE) || method.hasModifierProperty(PsiModifier.FINAL) + || method.isConstructor() || calledClass.hasModifierProperty(PsiModifier.FINAL)) { - final PsiCodeBlock body = method.getBody(); - return blockAssignsVariableOrFails(body, variable, - checkedMethods, strict); + return blockAssignsVariableOrFails(method.getBody(), variable, checkedMethods, strict); } return false; } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/security/DesignForExtensionInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/security/DesignForExtensionInspection.java index 7df6ad23aacd..5a90d4b3fad8 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/security/DesignForExtensionInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/security/DesignForExtensionInspection.java @@ -20,7 +20,7 @@ import com.intellij.psi.*; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jetbrains.annotations.NotNull; public class DesignForExtensionInspection extends BaseInspection { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/EmptySynchronizedStatementInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/EmptySynchronizedStatementInspection.java index 43060f727079..4a9e0f1dddac 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/EmptySynchronizedStatementInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/EmptySynchronizedStatementInspection.java @@ -21,7 +21,7 @@ import com.intellij.psi.PsiSynchronizedStatement; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jetbrains.annotations.NotNull; public class EmptySynchronizedStatementInspection extends BaseInspection { diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/MalformedFormatStringInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/MalformedFormatStringInspection.java new file mode 100644 index 000000000000..4f657fd6bfc9 --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/MalformedFormatStringInspection.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.bugs; + +import com.intellij.codeInspection.ui.ListTable; +import com.intellij.codeInspection.ui.ListWrappingTableModel; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.ui.UiUtils; + +import javax.swing.*; + +public class MalformedFormatStringInspection extends MalformedFormatStringInspectionBase { + @Override + public JComponent createOptionsPanel() { + ListWrappingTableModel classTableModel = + new ListWrappingTableModel(classNames, InspectionGadgetsBundle.message("string.format.class.column.name")); + JPanel classChooserPanel = UiUtils + .createAddRemoveTreeClassChooserPanel(new ListTable(classTableModel), InspectionGadgetsBundle.message("string.format.choose.class")); + + ListWrappingTableModel methodTableModel = + new ListWrappingTableModel(methodNames, InspectionGadgetsBundle.message("string.format.class.method.name")); + JPanel methodPanel = UiUtils.createAddRemovePanel(new ListTable(methodTableModel)); + + final JPanel panel = new JPanel(); + BoxLayout boxLayout = new BoxLayout(panel, BoxLayout.Y_AXIS); + panel.setLayout(boxLayout); + + panel.add(classChooserPanel); + panel.add(methodPanel); + return panel; + } +} diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspection.java index 9b5861c3b660..9b520199f90c 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspection.java @@ -24,7 +24,7 @@ import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.fixes.MoveClassFix; import com.siyeh.ig.psiutils.ClassUtils; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jetbrains.annotations.NotNull; public class ClassInTopLevelPackageInspection extends BaseInspection { diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassNameDiffersFromFileNameInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassNameDiffersFromFileNameInspection.java index cd7c3c0f7a26..9866dc66e421 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassNameDiffersFromFileNameInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassNameDiffersFromFileNameInspection.java @@ -23,7 +23,7 @@ import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.fixes.RenameFix; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/EmptyClassInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/EmptyClassInspection.java index 85b42f1ebd4b..85a6dff51fde 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/EmptyClassInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/EmptyClassInspection.java @@ -25,7 +25,7 @@ import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.fixes.AddToIgnoreIfAnnotatedByListQuickFix; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import com.siyeh.ig.ui.ExternalizableStringSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/errorhandling/BadExceptionDeclaredInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/errorhandling/BadExceptionDeclaredInspection.java index 98357a2e5020..5ce1f400d165 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/errorhandling/BadExceptionDeclaredInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/errorhandling/BadExceptionDeclaredInspection.java @@ -133,10 +133,8 @@ public class BadExceptionDeclaredInspection extends BaseInspection { super.visitMethod(method); if (ignoreTestCases) { final PsiClass containingClass = method.getContainingClass(); - if (containingClass != null && TestFrameworks.getInstance().isTestClass(containingClass)) { - return; - } - if (TestUtils.isJUnitTestMethod(method)) { + final TestFrameworks testFrameworks = TestFrameworks.getInstance(); + if (containingClass != null && testFrameworks.isTestOrConfig(containingClass)) { return; } } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/imports/StaticImportInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/imports/StaticImportInspection.java index 5defb344a335..e2a88f311c75 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/imports/StaticImportInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/imports/StaticImportInspection.java @@ -28,7 +28,7 @@ import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import com.siyeh.ig.psiutils.StringUtils; import com.siyeh.ig.psiutils.TestUtils; import com.siyeh.ig.ui.UiUtils; diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java index 4835464100b7..dc5802943dd8 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java @@ -27,4 +27,15 @@ public class MalformedFormatString { public void outOfMemory() { String.format("%2147483640$s", "s"); } + + public void optionalSettings() { + SomeOtherLogger logger = new SomeOtherLogger(); + logger.d("%s %s", 1); // this is invalid according to the inspector (correct) + } + + public class SomeOtherLogger { + public void d(String message, Object...args) { + // Do some logging. + } + } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/expected.xml index 7d528e94ae26..494e16062a4a 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/expected.xml +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/expected.xml @@ -56,10 +56,17 @@ <description>Too few arguments for format string "%s %s" + "hmm" #loc</description> </problem> - <problem> - <file>MalformedFormatString.java</file> - <line>28</line> - <problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Malformed format string</problem_class> - <description>Too few arguments for format string "%2147483640$s" #loc</description> - </problem> + <problem> + <file>MalformedFormatString.java</file> + <line>28</line> + <problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Malformed format string</problem_class> + <description>Too few arguments for format string "%2147483640$s" #loc</description> + </problem> + + <problem> + <file>MalformedFormatString.java</file> + <line>33</line> + <problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Malformed format string</problem_class> + <description>Too few arguments for format string "%s %s" #loc</description> + </problem> </problems>
\ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/initialization/field/InstanceVariableInitialization.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/initialization/field/InstanceVariableInitialization.java index 7973f523ecf5..e5ce63603346 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/initialization/field/InstanceVariableInitialization.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/initialization/field/InstanceVariableInitialization.java @@ -93,4 +93,10 @@ class C { C() { boolean b = (o = "") instanceof String; } +} +class D { + private java.util.List l; + D() { + for (Object o : l = new java.util.ArrayList()) {} + } }
\ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/MalformedFormatStringInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/MalformedFormatStringInspectionTest.java index 769a5fd94cab..6d0149c9d8f1 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/MalformedFormatStringInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/MalformedFormatStringInspectionTest.java @@ -5,7 +5,10 @@ import com.siyeh.ig.IGInspectionTestCase; public class MalformedFormatStringInspectionTest extends IGInspectionTestCase { public void test() throws Exception { - doTest("com/siyeh/igtest/bugs/malformed_format_string", - new MalformedFormatStringInspection()); + MalformedFormatStringInspection inspection = new MalformedFormatStringInspection(); + inspection.classNames.add("com.siyeh.igtest.bugs.malformed_format_string.MalformedFormatString.SomeOtherLogger"); + inspection.methodNames.add("d"); + + doTest("com/siyeh/igtest/bugs/malformed_format_string", inspection); } }
\ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/braces/RemoveBracesIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/braces/RemoveBracesIntention.java index b977e1c43cfd..e07e0dfe9108 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/braces/RemoveBracesIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/braces/RemoveBracesIntention.java @@ -17,7 +17,7 @@ package com.siyeh.ipp.braces; import com.intellij.psi.*; import com.intellij.util.IncorrectOperationException; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import com.siyeh.ipp.base.PsiElementPredicate; import org.jetbrains.annotations.NotNull; diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/conditional/ReplaceConditionalWithIfPredicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/conditional/ReplaceConditionalWithIfPredicate.java index 5fe305e286cc..597d95c422d9 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/conditional/ReplaceConditionalWithIfPredicate.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/conditional/ReplaceConditionalWithIfPredicate.java @@ -17,7 +17,7 @@ package com.siyeh.ipp.conditional; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; -import com.siyeh.ig.psiutils.FileTypeUtils; +import com.intellij.psi.util.FileTypeUtils; import com.siyeh.ipp.base.PsiElementPredicate; class ReplaceConditionalWithIfPredicate implements PsiElementPredicate { diff --git a/plugins/devkit/src/dom/impl/ExtensionDomExtender.java b/plugins/devkit/src/dom/impl/ExtensionDomExtender.java index eec055f3cc46..a76c166f43f8 100644 --- a/plugins/devkit/src/dom/impl/ExtensionDomExtender.java +++ b/plugins/devkit/src/dom/impl/ExtensionDomExtender.java @@ -41,6 +41,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.devkit.dom.*; +import java.lang.annotation.Annotation; import java.util.*; /** @@ -48,16 +49,70 @@ import java.util.*; */ public class ExtensionDomExtender extends DomExtender<Extensions> { private static final PsiClassConverter CLASS_CONVERTER = new PluginPsiClassConverter(); + + private static class MyRequired implements Required { + @Override + public boolean value() { + return true; + } + + @Override + public boolean nonEmpty() { + return true; + } + + @Override + public boolean identifier() { + return false; + } + + @Override + public Class<? extends Annotation> annotationType() { + return Required.class; + } + } + + private static class MyExtendClass extends ExtendClassImpl { + private final String myInterfaceName; + + private MyExtendClass(String interfaceName) { + myInterfaceName = interfaceName; + } + + @Override + public boolean allowAbstract() { + return false; + } + + @Override + public boolean allowInterface() { + return false; + } + + @Override + public boolean allowEnum() { + return false; + } + + @Override + public String value() { + return myInterfaceName; + } + } + private static final DomExtender EXTENSION_EXTENDER = new DomExtender() { public void registerExtensions(@NotNull final DomElement domElement, @NotNull final DomExtensionsRegistrar registrar) { final ExtensionPoint extensionPoint = (ExtensionPoint)domElement.getChildDescription().getDomDeclaration(); assert extensionPoint != null; - String interfaceName = extensionPoint.getInterface().getStringValue(); + final String interfaceName = extensionPoint.getInterface().getStringValue(); final Project project = extensionPoint.getManager().getProject(); if (interfaceName != null) { - registrar.registerGenericAttributeValueChildExtension(new XmlName("implementation"), PsiClass.class).setConverter(CLASS_CONVERTER); + registrar.registerGenericAttributeValueChildExtension(new XmlName("implementation"), PsiClass.class) + .setConverter(CLASS_CONVERTER) + .addCustomAnnotation(new MyExtendClass(interfaceName)) + .addCustomAnnotation(new MyRequired()); registerXmlb(registrar, JavaPsiFacade.getInstance(project).findClass(interfaceName, GlobalSearchScope.allScope(project)), Collections.<With>emptyList()); } diff --git a/plugins/devkit/src/projectRoots/IdeaJdk.java b/plugins/devkit/src/projectRoots/IdeaJdk.java index 6b40ced49796..a958af18d949 100644 --- a/plugins/devkit/src/projectRoots/IdeaJdk.java +++ b/plugins/devkit/src/projectRoots/IdeaJdk.java @@ -121,7 +121,7 @@ public class IdeaJdk extends JavaDependentSdkType implements JavaSdkType { } @Nullable - public final String getVersionString(final Sdk sdk) { + public final String getVersionString(@NotNull final Sdk sdk) { final Sdk internalJavaSdk = getInternalJavaSdk(sdk); return internalJavaSdk != null ? internalJavaSdk.getVersionString() : null; } @@ -401,13 +401,13 @@ public class IdeaJdk extends JavaDependentSdkType implements JavaSdkType { } @Nullable - public String getBinPath(Sdk sdk) { + public String getBinPath(@NotNull Sdk sdk) { final Sdk internalJavaSdk = getInternalJavaSdk(sdk); return internalJavaSdk == null ? null : JavaSdk.getInstance().getBinPath(internalJavaSdk); } @Nullable - public String getToolsPath(Sdk sdk) { + public String getToolsPath(@NotNull Sdk sdk) { final Sdk jdk = getInternalJavaSdk(sdk); if (jdk != null && jdk.getVersionString() != null){ return JavaSdk.getInstance().getToolsPath(jdk); @@ -416,12 +416,12 @@ public class IdeaJdk extends JavaDependentSdkType implements JavaSdkType { } @Nullable - public String getVMExecutablePath(Sdk sdk) { + public String getVMExecutablePath(@NotNull Sdk sdk) { final Sdk internalJavaSdk = getInternalJavaSdk(sdk); return internalJavaSdk == null ? null : JavaSdk.getInstance().getVMExecutablePath(internalJavaSdk); } - public void saveAdditionalData(SdkAdditionalData additionalData, Element additional) { + public void saveAdditionalData(@NotNull SdkAdditionalData additionalData, @NotNull Element additional) { if (additionalData instanceof Sandbox) { try { ((Sandbox)additionalData).writeExternal(additional); @@ -432,7 +432,7 @@ public class IdeaJdk extends JavaDependentSdkType implements JavaSdkType { } } - public SdkAdditionalData loadAdditionalData(Sdk sdk, Element additional) { + public SdkAdditionalData loadAdditionalData(@NotNull Sdk sdk, Element additional) { Sandbox sandbox = new Sandbox(sdk); try { sandbox.readExternal(additional); diff --git a/plugins/devkit/testData/codeInsight/ExtensionQualifiedName.xml b/plugins/devkit/testData/codeInsight/ExtensionQualifiedName.xml index e74bde491efe..d5b03f845235 100644 --- a/plugins/devkit/testData/codeInsight/ExtensionQualifiedName.xml +++ b/plugins/devkit/testData/codeInsight/ExtensionQualifiedName.xml @@ -6,7 +6,12 @@ </extensionPoints> <extensions defaultExtensionNs="com.intellij.myPlugin"> - <ext implementation="java.lang.Runnable"/> + <<error descr="'implementation' attribute should be defined">ext</error>/> + <ext implementation="<error descr="Interface not allowed">java.lang.Runnable</error>"/> + <ext implementation="<error descr="'java.util.concurrent.TimeUnit' is not assignable to 'java.lang.Runnable'"><error descr="Enum not allowed">java.util.concurrent.TimeUnit</error></error>"/> + <ext implementation="<error descr="'java.lang.String' is not assignable to 'java.lang.Runnable'">java.lang.String</error>"/> + + <ext implementation="foo.MyRunnable"/> </extensions> </idea-plugin>
\ No newline at end of file diff --git a/plugins/devkit/testSources/codeInsight/PluginXmlFunctionalTest.groovy b/plugins/devkit/testSources/codeInsight/PluginXmlFunctionalTest.groovy index 52c78570200c..1ba75d4622e6 100644 --- a/plugins/devkit/testSources/codeInsight/PluginXmlFunctionalTest.groovy +++ b/plugins/devkit/testSources/codeInsight/PluginXmlFunctionalTest.groovy @@ -117,6 +117,7 @@ public class PluginXmlFunctionalTest extends JavaCodeInsightFixtureTestCase { } public void testExtensionQualifiedName() throws Throwable { + myFixture.addClass("package foo; public class MyRunnable implements java.lang.Runnable {}"); configureByFile(); myFixture.checkHighlighting(false, false, false); } diff --git a/plugins/git4idea/src/git4idea/commands/GitSimpleHandler.java b/plugins/git4idea/src/git4idea/commands/GitSimpleHandler.java index 6cef328bfa2a..873b395e34ea 100644 --- a/plugins/git4idea/src/git4idea/commands/GitSimpleHandler.java +++ b/plugins/git4idea/src/git4idea/commands/GitSimpleHandler.java @@ -133,8 +133,7 @@ public class GitSimpleHandler extends GitTextHandler { return; } entire.append(text); - if (suppressed || myVcs == null) { - LOG.debug(text); + if (myVcs == null || (suppressed && !LOG.isDebugEnabled())) { return; } int last = lineRest.length() > 0 ? lineRest.charAt(lineRest.length() - 1) : -1; @@ -158,13 +157,19 @@ public class GitSimpleHandler extends GitTextHandler { else { line = text.substring(start, savedPos); } - if (ProcessOutputTypes.STDOUT == outputType && !StringUtil.isEmptyOrSpaces(line)) { - myVcs.showMessages(line); - LOG.info(line.trim()); - } - else if (ProcessOutputTypes.STDERR == outputType && !StringUtil.isEmptyOrSpaces(line)) { - myVcs.showErrorMessages(line); - LOG.info(line.trim()); + if (!StringUtil.isEmptyOrSpaces(line)) { + if (!suppressed) { + LOG.info(line.trim()); + if (ProcessOutputTypes.STDOUT == outputType) { + myVcs.showMessages(line); + } + else if (ProcessOutputTypes.STDERR == outputType) { + myVcs.showErrorMessages(line); + } + } + else { + LOG.debug(line.trim()); + } } } start = savedPos; diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/GithubApiUtil.java b/plugins/github/src/org/jetbrains/plugins/github/api/GithubApiUtil.java index 2f634d08b939..98540470aadc 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/api/GithubApiUtil.java +++ b/plugins/github/src/org/jetbrains/plugins/github/api/GithubApiUtil.java @@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.github.exceptions.GithubAuthenticationException; import org.jetbrains.plugins.github.exceptions.GithubJsonException; +import org.jetbrains.plugins.github.exceptions.GithubRateLimitExceededException; import org.jetbrains.plugins.github.exceptions.GithubStatusCodeException; import org.jetbrains.plugins.github.util.GithubAuthData; import org.jetbrains.plugins.github.util.GithubSslSupport; @@ -209,7 +210,11 @@ public class GithubApiUtil { case HttpStatus.SC_UNAUTHORIZED: case HttpStatus.SC_PAYMENT_REQUIRED: case HttpStatus.SC_FORBIDDEN: - throw new GithubAuthenticationException("Request response: " + getErrorMessage(method)); + String message = getErrorMessage(method); + if (message.contains("API rate limit exceeded")) { + throw new GithubRateLimitExceededException(message); + } + throw new GithubAuthenticationException("Request response: " + message); default: throw new GithubStatusCodeException(code + ": " + getErrorMessage(method), code); } @@ -535,11 +540,15 @@ public class GithubApiUtil { return createDataFromRaw(fromJson(postRequest(auth, path, gson.toJson(request)), GithubRepoRaw.class), GithubRepo.class); } + /* + * Open issues only + */ @NotNull public static List<GithubIssue> getIssuesAssigned(@NotNull GithubAuthData auth, @NotNull String user, @NotNull String repo, - @Nullable String assigned) throws IOException { + @Nullable String assigned, + int max) throws IOException { String path; if (StringUtil.isEmptyOrSpaces(assigned)) { path = "/repos/" + user + "/" + repo + "/issues?" + PER_PAGE; @@ -550,10 +559,17 @@ public class GithubApiUtil { PagedRequest<GithubIssue> request = new PagedRequest<GithubIssue>(path, GithubIssue.class, GithubIssueRaw[].class); - return request.getAll(auth); + List<GithubIssue> result = new ArrayList<GithubIssue>(); + while (request.hasNext() && max > result.size()) { + result.addAll(request.next(auth)); + } + return result; } @NotNull + /* + * All issues - open and closed + */ public static List<GithubIssue> getIssuesQueried(@NotNull GithubAuthData auth, @NotNull String user, @NotNull String repo, @@ -601,6 +617,32 @@ public class GithubApiUtil { } @NotNull + public static List<GithubCommitComment> getCommitComments(@NotNull GithubAuthData auth, + @NotNull String user, + @NotNull String repo, + @NotNull String sha) throws IOException { + String path = "/repos/" + user + "/" + repo + "/commits/" + sha + "/comments"; + + PagedRequest<GithubCommitComment> request = + new PagedRequest<GithubCommitComment>(path, GithubCommitComment.class, GithubCommitCommentRaw[].class, ACCEPT_HTML_BODY_MARKUP); + + return request.getAll(auth); + } + + @NotNull + public static List<GithubCommitComment> getPullRequestComments(@NotNull GithubAuthData auth, + @NotNull String user, + @NotNull String repo, + long id) throws IOException { + String path = "/repos/" + user + "/" + repo + "/pulls/" + id + "/comments"; + + PagedRequest<GithubCommitComment> request = + new PagedRequest<GithubCommitComment>(path, GithubCommitComment.class, GithubCommitCommentRaw[].class, ACCEPT_HTML_BODY_MARKUP); + + return request.getAll(auth); + } + + @NotNull public static GithubPullRequest getPullRequest(@NotNull GithubAuthData auth, @NotNull String user, @NotNull String repo, int id) throws IOException { String path = "/repos/" + user + "/" + repo + "/pulls/" + id; diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/GithubCommitComment.java b/plugins/github/src/org/jetbrains/plugins/github/api/GithubCommitComment.java new file mode 100644 index 000000000000..1f13de7bb57f --- /dev/null +++ b/plugins/github/src/org/jetbrains/plugins/github/api/GithubCommitComment.java @@ -0,0 +1,102 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.github.api; + +import org.jetbrains.annotations.NotNull; + +import java.util.Date; + +/** + * @author Aleksey Pivovarov + */ +@SuppressWarnings("UnusedDeclaration") +public class GithubCommitComment { + @NotNull private final String myHtmlUrl; + + private final long myId; + @NotNull private final String mySha; + @NotNull private final String myPath; + private final long myPosition; // line number in diff + @NotNull private final String myBodyHtml; + + @NotNull private final GithubUser myUser; + + @NotNull private final Date myCreatedAt; + @NotNull private final Date myUpdatedAt; + + public GithubCommitComment(@NotNull String htmlUrl, + long id, + @NotNull String sha, + @NotNull String path, + long position, + @NotNull String bodyHtml, + @NotNull GithubUser user, + @NotNull Date createdAt, + @NotNull Date updatedAt) { + myHtmlUrl = htmlUrl; + myId = id; + mySha = sha; + myPath = path; + myPosition = position; + myBodyHtml = bodyHtml; + myUser = user; + myCreatedAt = createdAt; + myUpdatedAt = updatedAt; + } + + @NotNull + public String getHtmlUrl() { + return myHtmlUrl; + } + + public long getId() { + return myId; + } + + @NotNull + public String getSha() { + return mySha; + } + + @NotNull + public String getPath() { + return myPath; + } + + public long getPosition() { + return myPosition; + } + + @NotNull + public String getBodyHtml() { + return myBodyHtml; + } + + @NotNull + public GithubUser getUser() { + return myUser; + } + + @NotNull + public Date getCreatedAt() { + return myCreatedAt; + } + + @NotNull + public Date getUpdatedAt() { + return myUpdatedAt; + } +} diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/GithubCommitCommentRaw.java b/plugins/github/src/org/jetbrains/plugins/github/api/GithubCommitCommentRaw.java new file mode 100644 index 000000000000..229b31ab92eb --- /dev/null +++ b/plugins/github/src/org/jetbrains/plugins/github/api/GithubCommitCommentRaw.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.github.api; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Date; + +/** + * @author Aleksey Pivovarov + */ +class GithubCommitCommentRaw implements DataConstructor { + @Nullable public String htmlUrl; + @Nullable public String url; + + @Nullable public Long id; + @Nullable public String commitId; + @Nullable public String path; + @Nullable public Long position; + @Nullable public Long line; + @Nullable public String body; + @Nullable public String bodyHtml; + + @Nullable public GithubUserRaw user; + + @Nullable public Date createdAt; + @Nullable public Date updatedAt; + + @SuppressWarnings("ConstantConditions") + @NotNull + public GithubCommitComment createCommitComment() { + return new GithubCommitComment(htmlUrl, id, commitId, path, position, bodyHtml, user.createUser(), createdAt, updatedAt); + } + + @SuppressWarnings("unchecked") + @NotNull + @Override + public <T> T create(@NotNull Class<T> resultClass) { + if (resultClass.isAssignableFrom(GithubCommitComment.class)) { + return (T)createCommitComment(); + } + + throw new ClassCastException(this.getClass().getName() + ": bad class type: " + resultClass.getName()); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnPropDetailsProvider.java b/plugins/github/src/org/jetbrains/plugins/github/exceptions/GithubRateLimitExceededException.java index 683024679277..66272f466e69 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnPropDetailsProvider.java +++ b/plugins/github/src/org/jetbrains/plugins/github/exceptions/GithubRateLimitExceededException.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.properties; +package org.jetbrains.plugins.github.exceptions; + +import java.io.IOException; /** - * Created with IntelliJ IDEA. - * User: Irina.Chernushina - * Date: 2/12/12 - * Time: 8:40 PM + * @author Aleksey Pivovarov */ -public class SvnPropDetailsProvider { +public class GithubRateLimitExceededException extends IOException { + public GithubRateLimitExceededException(String message) { + super(message); + } } diff --git a/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepository.java b/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepository.java index f4e45544dd8e..b975956e3eea 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepository.java +++ b/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepository.java @@ -20,6 +20,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.github.api.GithubApiUtil; import org.jetbrains.plugins.github.exceptions.GithubAuthenticationException; import org.jetbrains.plugins.github.exceptions.GithubJsonException; +import org.jetbrains.plugins.github.exceptions.GithubRateLimitExceededException; import org.jetbrains.plugins.github.exceptions.GithubStatusCodeException; import org.jetbrains.plugins.github.util.GithubAuthData; import org.jetbrains.plugins.github.util.GithubUtil; @@ -85,7 +86,10 @@ public class GithubRepository extends BaseRepositoryImpl { @Override public Task[] getIssues(@Nullable String query, int max, long since) throws Exception { try { - return getIssues(query); + return getIssues(query, max); + } + catch (GithubRateLimitExceededException e) { + return new Task[0]; } catch (GithubAuthenticationException e) { throw new Exception(e.getMessage(), e); @@ -99,13 +103,13 @@ public class GithubRepository extends BaseRepositoryImpl { } @NotNull - private Task[] getIssues(@Nullable String query) throws Exception { + private Task[] getIssues(@Nullable String query, int max) throws Exception { List<GithubIssue> issues; if (StringUtil.isEmptyOrSpaces(query)) { if (StringUtil.isEmptyOrSpaces(myUser)) { myUser = GithubApiUtil.getCurrentUser(getAuthData()).getLogin(); } - issues = GithubApiUtil.getIssuesAssigned(getAuthData(), getRepoAuthor(), getRepoName(), myUser); + issues = GithubApiUtil.getIssuesAssigned(getAuthData(), getRepoAuthor(), getRepoName(), myUser, max); } else { issues = GithubApiUtil.getIssuesQueried(getAuthData(), getRepoAuthor(), getRepoName(), query); @@ -266,6 +270,7 @@ public class GithubRepository extends BaseRepositoryImpl { public void setToken(@NotNull String token) { myToken = token; + setUser(""); } @Tag("token") diff --git a/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepositoryEditor.java b/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepositoryEditor.java index 2a83c85a2d1f..c9269436b738 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepositoryEditor.java +++ b/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepositoryEditor.java @@ -65,7 +65,6 @@ public class GithubRepositoryEditor extends BaseRepositoryEditor<GithubRepositor myHost.getDocument().addDocumentListener(buttonUpdater); myRepoAuthor.getDocument().addDocumentListener(buttonUpdater); myRepoName.getDocument().addDocumentListener(buttonUpdater); - myURLText.getDocument().addDocumentListener(buttonUpdater); } @Nullable diff --git a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.form b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.form index b540bd9c3ea5..b166fce1ef38 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.form +++ b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.form @@ -47,7 +47,9 @@ </component> <component id="5a680" class="com.intellij.openapi.ui.ComboBox" binding="myAuthTypeComboBox"> <constraints> - <grid row="0" column="3" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/> + <grid row="0" column="3" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"> + <preferred-size width="120" height="-1"/> + </grid> </constraints> <properties/> </component> @@ -143,7 +145,7 @@ <text value="Test"/> </properties> </component> - <component id="b276a" class="com.intellij.ui.components.JBLabel"> + <component id="b276a" class="com.intellij.ui.components.JBLabel" binding="myAuthTypeLabel"> <constraints> <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/> </constraints> diff --git a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.java b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.java index 365fa2f36b2b..26cbbf6f539a 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.java +++ b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.java @@ -33,6 +33,7 @@ import org.jetbrains.plugins.github.util.GithubUtil; import org.jetbrains.plugins.github.api.GithubUser; import javax.swing.*; +import javax.swing.border.EmptyBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.HyperlinkEvent; @@ -64,6 +65,7 @@ public class GithubSettingsPanel { private JTextField myHostTextField; private ComboBox myAuthTypeComboBox; private JPanel myCardPanel; + private JBLabel myAuthTypeLabel; private boolean myCredentialsModified; @@ -75,10 +77,10 @@ public class GithubSettingsPanel { BrowserUtil.browse(e.getURL()); } }); - mySignupTextField.setText( - "<html>Do not have an account at github.com? <a href=\"https://github.com\">" + "Sign up" + "</a></html>"); + mySignupTextField.setText("<html>Do not have an account at github.com? <a href=\"https://github.com\">" + "Sign up" + "</a></html>"); mySignupTextField.setBackground(myPane.getBackground()); mySignupTextField.setCursor(new Cursor(Cursor.HAND_CURSOR)); + myAuthTypeLabel.setBorder(new EmptyBorder(0, 10, 0, 0)); myAuthTypeComboBox.addItem(AUTH_PASSWORD); myAuthTypeComboBox.addItem(AUTH_TOKEN); diff --git a/plugins/github/test/org/jetbrains/plugins/github/GithubCreateGistContentTest.java b/plugins/github/test/org/jetbrains/plugins/github/GithubCreateGistContentTest.java index e5bc04b2f975..f5cae2a1a241 100644 --- a/plugins/github/test/org/jetbrains/plugins/github/GithubCreateGistContentTest.java +++ b/plugins/github/test/org/jetbrains/plugins/github/GithubCreateGistContentTest.java @@ -30,9 +30,9 @@ import static org.jetbrains.plugins.github.api.GithubGist.FileContent; * @author Aleksey Pivovarov */ public class GithubCreateGistContentTest extends GithubCreateGistTestBase { + @Override - public void setUp() throws Exception { - super.setUp(); + protected void beforeTest() throws Exception { createProjectFiles(); } diff --git a/plugins/github/test/org/jetbrains/plugins/github/GithubCreateGistTestBase.java b/plugins/github/test/org/jetbrains/plugins/github/GithubCreateGistTestBase.java index b564267f91a8..150e7e89d85d 100644 --- a/plugins/github/test/org/jetbrains/plugins/github/GithubCreateGistTestBase.java +++ b/plugins/github/test/org/jetbrains/plugins/github/GithubCreateGistTestBase.java @@ -38,20 +38,14 @@ public abstract class GithubCreateGistTestBase extends GithubTest { protected String GIST_DESCRIPTION; @Override - public void setUp() throws Exception { - super.setUp(); + protected void beforeTest() throws Exception { long time = Clock.getTime(); GIST_DESCRIPTION = getTestName(false) + "_" + DateFormatUtil.formatDate(time); } @Override - public void tearDown() throws Exception { - try { - deleteGist(); - } - finally { - super.tearDown(); - } + protected void afterTest() throws Exception { + deleteGist(); } protected void deleteGist() throws IOException { diff --git a/plugins/github/test/org/jetbrains/plugins/github/GithubCreatePullRequestTestBase.java b/plugins/github/test/org/jetbrains/plugins/github/GithubCreatePullRequestTestBase.java index 24bb7800447c..9db5ba8a0b1d 100644 --- a/plugins/github/test/org/jetbrains/plugins/github/GithubCreatePullRequestTestBase.java +++ b/plugins/github/test/org/jetbrains/plugins/github/GithubCreatePullRequestTestBase.java @@ -41,9 +41,7 @@ public abstract class GithubCreatePullRequestTestBase extends GithubTest { protected String BRANCH_NAME; @Override - public void setUp() throws Exception { - super.setUp(); - + protected void beforeTest() throws Exception { Random rnd = new Random(); long time = Clock.getTime(); BRANCH_NAME = "branch_" + getTestName(false) + "_" + DateFormatUtil.formatDate(time).replace('/', '-') + "_" + rnd.nextLong(); @@ -58,13 +56,8 @@ public abstract class GithubCreatePullRequestTestBase extends GithubTest { } @Override - public void tearDown() throws Exception { - try { - deleteRemoteBranch(); - } - finally { - super.tearDown(); - } + protected void afterTest() throws Exception { + deleteRemoteBranch(); } protected void deleteRemoteBranch() { diff --git a/plugins/github/test/org/jetbrains/plugins/github/GithubIssuesTest.java b/plugins/github/test/org/jetbrains/plugins/github/GithubIssuesTest.java new file mode 100644 index 000000000000..1b471691107d --- /dev/null +++ b/plugins/github/test/org/jetbrains/plugins/github/GithubIssuesTest.java @@ -0,0 +1,103 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.github; + +import com.intellij.openapi.util.Comparing; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.plugins.github.api.GithubApiUtil; +import org.jetbrains.plugins.github.api.GithubIssue; +import org.jetbrains.plugins.github.test.GithubTest; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Aleksey Pivovarov + */ +public class GithubIssuesTest extends GithubTest { + private static final String REPO_NAME = "IssuesTest"; + + public void testAssigneeIssues1() throws Exception { + List<GithubIssue> result = GithubApiUtil.getIssuesAssigned(myAuth, myLogin2, REPO_NAME, myLogin1, 100); + List<Long> issues = ContainerUtil.map(result, new Function<GithubIssue, Long>() { + @Override + public Long fun(GithubIssue githubIssue) { + return githubIssue.getNumber(); + } + }); + + List<Long> expected = Arrays.asList(6L, 7L, 8L); + + assertTrue(Comparing.haveEqualElements(issues, expected)); + } + + public void testAssigneeIssues2() throws Exception { + List<GithubIssue> result = GithubApiUtil.getIssuesAssigned(myAuth, myLogin2, REPO_NAME, myLogin2, 100); + List<Long> issues = ContainerUtil.map(result, new Function<GithubIssue, Long>() { + @Override + public Long fun(GithubIssue githubIssue) { + return githubIssue.getNumber(); + } + }); + + List<Long> expected = Arrays.asList(1L, 2L); + + assertTrue(Comparing.haveEqualElements(issues, expected)); + } + + public void testAssigneeIssues3() throws Exception { + List<GithubIssue> result = GithubApiUtil.getIssuesAssigned(myAuth, myLogin2, REPO_NAME, "", 100); + List<Long> issues = ContainerUtil.map(result, new Function<GithubIssue, Long>() { + @Override + public Long fun(GithubIssue githubIssue) { + return githubIssue.getNumber(); + } + }); + + List<Long> expected = Arrays.asList(1L, 2L, 5L, 6L, 7L, 8L, 9L, 10L, 11L); + + assertTrue(Comparing.haveEqualElements(issues, expected)); + } + + public void testQueriedIssues1() throws Exception { + List<GithubIssue> result = GithubApiUtil.getIssuesQueried(myAuth, myLogin2, REPO_NAME, "abracadabra"); + List<Long> issues = ContainerUtil.map(result, new Function<GithubIssue, Long>() { + @Override + public Long fun(GithubIssue githubIssue) { + return githubIssue.getNumber(); + } + }); + + List<Long> expected = Arrays.asList(10L, 12L); + + assertContainsElements(issues, expected); + } + + public void testQueriedIssues2() throws Exception { + List<GithubIssue> result = GithubApiUtil.getIssuesQueried(myAuth, myLogin2, REPO_NAME, "commentary"); + List<Long> issues = ContainerUtil.map(result, new Function<GithubIssue, Long>() { + @Override + public Long fun(GithubIssue githubIssue) { + return githubIssue.getNumber(); + } + }); + + List<Long> expected = Arrays.asList(11L); + + assertContainsElements(issues, expected); + } +} diff --git a/plugins/github/test/org/jetbrains/plugins/github/GithubRequestPagingTest.java b/plugins/github/test/org/jetbrains/plugins/github/GithubRequestPagingTest.java index c72cbc3c2b4a..4fec0d1c7a5c 100644 --- a/plugins/github/test/org/jetbrains/plugins/github/GithubRequestPagingTest.java +++ b/plugins/github/test/org/jetbrains/plugins/github/GithubRequestPagingTest.java @@ -28,9 +28,9 @@ import static org.junit.Assume.assumeNotNull; * @author Aleksey Pivovarov */ public class GithubRequestPagingTest extends GithubTest { + @Override - protected void setUp() throws Exception { - super.setUp(); + protected void beforeTest() throws Exception { assumeNotNull(myLogin2); } diff --git a/plugins/github/test/org/jetbrains/plugins/github/GithubShareProjectTestBase.java b/plugins/github/test/org/jetbrains/plugins/github/GithubShareProjectTestBase.java index 4eee8e1439f4..5dd9b218dfed 100644 --- a/plugins/github/test/org/jetbrains/plugins/github/GithubShareProjectTestBase.java +++ b/plugins/github/test/org/jetbrains/plugins/github/GithubShareProjectTestBase.java @@ -35,9 +35,7 @@ public abstract class GithubShareProjectTestBase extends GithubTest { protected String PROJECT_NAME; @Override - public void setUp() throws Exception { - super.setUp(); - + protected void beforeTest() throws Exception { Random rnd = new Random(); long time = Clock.getTime(); PROJECT_NAME = "new_project_from_" + getTestName(false) + "_" + DateFormatUtil.formatDate(time).replace('/', '-') + "_" + rnd.nextLong(); @@ -45,13 +43,8 @@ public abstract class GithubShareProjectTestBase extends GithubTest { } @Override - public void tearDown() throws Exception { - try { - deleteGithubRepo(); - } - finally { - super.tearDown(); - } + protected void afterTest() throws Exception { + deleteGithubRepo(); } protected void deleteGithubRepo() throws IOException { diff --git a/plugins/github/test/org/jetbrains/plugins/github/test/GithubTest.java b/plugins/github/test/org/jetbrains/plugins/github/test/GithubTest.java index 0f2da3848c38..2c9496e9a27e 100644 --- a/plugins/github/test/org/jetbrains/plugins/github/test/GithubTest.java +++ b/plugins/github/test/org/jetbrains/plugins/github/test/GithubTest.java @@ -56,9 +56,11 @@ import static org.junit.Assume.assumeNotNull; * <li>Project base directory is the root of everything. </li> * </ul></p> * <p>All tests inherited from this class are required to have a login and a password to access the Github server. - * They are set up in System properties: <br/> - * <code>-Dtest.github.login=mylogin<br/> - * -Dtest.github.password=mypassword</code> + * They are set up in Environment variables: <br/> + * <code>idea.test.github.host=myHost<br/> + * idea.test.github.login1=mylogin1<br/> // test user + * idea.test.github.login2=mylogin2<br/> // user with configured test repositories + * idea.test.github.password=mypassword</code> // password for test user * </p> * * @author Kirill Likhodedov @@ -173,7 +175,7 @@ public abstract class GithubTest extends UsefulTestCase { } @Override - protected void setUp() throws Exception { + protected final void setUp() throws Exception { final String host = System.getenv("idea.test.github.host"); final String login1 = System.getenv("idea.test.github.login1"); final String login2 = System.getenv("idea.test.github.login2"); @@ -186,8 +188,14 @@ public abstract class GithubTest extends UsefulTestCase { super.setUp(); - myProjectFixture = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getTestName(true)).getFixture(); - myProjectFixture.setUp(); + try { + myProjectFixture = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getTestName(true)).getFixture(); + myProjectFixture.setUp(); + } + catch (Exception e) { + super.tearDown(); + throw e; + } myProject = myProjectFixture.getProject(); myProjectRoot = myProject.getBaseDir(); @@ -209,17 +217,39 @@ public abstract class GithubTest extends UsefulTestCase { myHttpAuthService = (GitHttpAuthTestService)ServiceManager.getService(GitHttpAuthService.class); myGitRepositoryManager = GitUtil.getRepositoryManager(myProject); + + try { + beforeTest(); + } + catch (Exception e) { + try { + tearDown(); + } + catch (Exception e2) { + e2.printStackTrace(); + } + throw e; + } } @Override - protected void tearDown() throws Exception { - myHttpAuthService.cleanup(); - myDialogManager.cleanup(); - myNotificator.cleanup(); + protected final void tearDown() throws Exception { + try { + afterTest(); + } + finally { + myHttpAuthService.cleanup(); + myDialogManager.cleanup(); + myNotificator.cleanup(); - myProjectFixture.tearDown(); - super.tearDown(); + myProjectFixture.tearDown(); + super.tearDown(); + } } + protected void beforeTest() throws Exception { + } + protected void afterTest() throws Exception { + } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleProjectSettingsControl.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleProjectSettingsControl.java index 669bc3a5317b..53675561a5bc 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleProjectSettingsControl.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleProjectSettingsControl.java @@ -111,8 +111,8 @@ public class GradleProjectSettingsControl extends AbstractExternalProjectSetting initControls(); content.add(myUseWrapperButton, ExternalSystemUiUtil.getFillLineConstraints(indentLevel)); - content.add(myUseLocalDistributionButton, ExternalSystemUiUtil.getFillLineConstraints(indentLevel)); content.add(myUseBundledDistributionButton, ExternalSystemUiUtil.getFillLineConstraints(indentLevel)); + content.add(myUseLocalDistributionButton, ExternalSystemUiUtil.getFillLineConstraints(indentLevel)); content.add(myGradleHomeLabel, ExternalSystemUiUtil.getLabelConstraints(indentLevel)); content.add(myGradleHomePathField, ExternalSystemUiUtil.getFillLineConstraints(0)); diff --git a/plugins/groovy/jetgroovy.iml b/plugins/groovy/jetgroovy.iml index d8531aeafeda..bd8506caffbb 100644 --- a/plugins/groovy/jetgroovy.iml +++ b/plugins/groovy/jetgroovy.iml @@ -34,6 +34,7 @@ <orderEntry type="module" module-name="junit" scope="TEST" /> <orderEntry type="module" module-name="java-indexing-api" /> <orderEntry type="module" module-name="groovy-jps-plugin" /> + <orderEntry type="module" module-name="ByteCodeViewer" /> </component> </module> diff --git a/plugins/groovy/resources/intentionDescriptions/GrRemoveExplicitTypeDeclarationIntention/after.groovy.template b/plugins/groovy/resources/intentionDescriptions/GrRemoveExplicitTypeDeclarationIntention/after.groovy.template new file mode 100644 index 000000000000..1862911cef6e --- /dev/null +++ b/plugins/groovy/resources/intentionDescriptions/GrRemoveExplicitTypeDeclarationIntention/after.groovy.template @@ -0,0 +1 @@ +<spot>def</spot> list = ['a', 'b', 'c']
\ No newline at end of file diff --git a/plugins/groovy/resources/intentionDescriptions/GrRemoveExplicitTypeDeclarationIntention/before.groovy.template b/plugins/groovy/resources/intentionDescriptions/GrRemoveExplicitTypeDeclarationIntention/before.groovy.template new file mode 100644 index 000000000000..9cc469cab515 --- /dev/null +++ b/plugins/groovy/resources/intentionDescriptions/GrRemoveExplicitTypeDeclarationIntention/before.groovy.template @@ -0,0 +1 @@ +<spot>ArrayList<String></spot> list = ['a', 'b', 'c']
\ No newline at end of file diff --git a/plugins/groovy/resources/intentionDescriptions/GrRemoveExplicitTypeDeclarationIntention/description.html b/plugins/groovy/resources/intentionDescriptions/GrRemoveExplicitTypeDeclarationIntention/description.html new file mode 100644 index 000000000000..f3c976cc242d --- /dev/null +++ b/plugins/groovy/resources/intentionDescriptions/GrRemoveExplicitTypeDeclarationIntention/description.html @@ -0,0 +1,5 @@ +<html> +<body> +This intention removes explicit type elements from variables and methods +</body> +</html>
\ No newline at end of file diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index fa0fffb25f13..143c787d4c0e 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -26,6 +26,7 @@ <depends optional="true" config-file="intellilang-groovy-support.xml">org.intellij.intelliLang</depends> <depends optional="true">AntSupport</depends> <depends optional="true">cucumber</depends> + <depends optional="true">ByteCodeViewer</depends> <extensionPoints> <extensionPoint name="methodComparator" interface="org.jetbrains.plugins.groovy.lang.resolve.GrMethodComparator"/> @@ -1391,6 +1392,11 @@ <categoryKey>intention.category.groovy/intention.category.groovy.other</categoryKey> <className>org.jetbrains.plugins.groovy.intentions.other.GrCopyStringConcatenationContentIntention</className> </intentionAction> + <intentionAction> + <bundleName>org.jetbrains.plugins.groovy.intentions.GroovyIntentionsBundle</bundleName> + <categoryKey>intention.category.groovy/intention.category.groovy.declaration</categoryKey> + <className>org.jetbrains.plugins.groovy.intentions.declaration.GrRemoveExplicitTypeDeclarationIntention</className> + </intentionAction> <projectService serviceInterface="org.jetbrains.plugins.groovy.annotator.intentions.dynamic.DynamicToolWindowWrapper" serviceImplementation="org.jetbrains.plugins.groovy.annotator.intentions.dynamic.DynamicToolWindowWrapper"/> @@ -1429,6 +1435,10 @@ <codeFragmentFactory implementation="org.jetbrains.plugins.groovy.debugger.GroovyCodeFragmentFactory"/> </extensions> + <extensions defaultExtensionNs="ByteCodeViewer"> + <classSearcher implementation="org.jetbrains.plugins.groovy.byteCodeViewer.GroovyScriptClassSearcher"/> + </extensions> + <actions> <action id="Groovy.Shell.Execute" class="com.intellij.openapi.actionSystem.EmptyAction" text="Execute Groovy Code" description="Execute Groovy code in console"> diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/byteCodeViewer/GroovyScriptClassSearcher.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/byteCodeViewer/GroovyScriptClassSearcher.java new file mode 100644 index 000000000000..3365dc54f5c5 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/byteCodeViewer/GroovyScriptClassSearcher.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.groovy.byteCodeViewer; + +import com.intellij.byteCodeViewer.ClassSearcher; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiTypeParameter; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; + +/** + * Created by Max Medvedev on 8/23/13 + */ +public class GroovyScriptClassSearcher implements ClassSearcher { + @Nullable + @Override + public PsiClass findClass(@NotNull PsiElement place) { + if (place.getLanguage() == GroovyFileType.GROOVY_LANGUAGE) { + PsiClass containingClass = PsiTreeUtil.getParentOfType(place, PsiClass.class, false); + while (containingClass instanceof PsiTypeParameter) { + containingClass = PsiTreeUtil.getParentOfType(containingClass, PsiClass.class); + } + if (containingClass != null) return containingClass; + + PsiFile file = place.getContainingFile(); + if (file instanceof GroovyFile && ((GroovyFile)file).isScript()) { + return ((GroovyFile)file).getScriptClass(); + } + } + return null; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/confusing/ClashingGettersInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/confusing/ClashingGettersInspection.java index 9570fb633f74..c66b9673dde8 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/confusing/ClashingGettersInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/confusing/ClashingGettersInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,6 +57,7 @@ public class ClashingGettersInspection extends BaseInspection { return GroovyInspectionBundle.message("getter.0.clashes.with.getter.1", args); } + @NotNull @Override protected BaseInspectionVisitor buildVisitor() { return new BaseInspectionVisitor() { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyAnnotationNamingConventionInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyAnnotationNamingConventionInspection.java index 62b21ed08d85..dfaca8f5081c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyAnnotationNamingConventionInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyAnnotationNamingConventionInspection.java @@ -20,7 +20,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor; import org.jetbrains.plugins.groovy.codeInspection.GroovyFix; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnnotationTypeDefinition; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; public class GroovyAnnotationNamingConventionInspection extends ConventionInspection { @@ -32,7 +31,7 @@ public class GroovyAnnotationNamingConventionInspection extends ConventionInspec return "Annotation naming convention"; } - protected GroovyFix buildFix(PsiElement location) { + protected GroovyFix buildFix(@NotNull PsiElement location) { return new RenameFix(); } @@ -63,18 +62,15 @@ public class GroovyAnnotationNamingConventionInspection extends ConventionInspec return DEFAULT_MAX_LENGTH; } + @NotNull public BaseInspectionVisitor buildVisitor() { return new NamingConventionsVisitor(); } private class NamingConventionsVisitor extends BaseInspectionVisitor { - - public void visitTypeDefinition(GrTypeDefinition grTypeDefinition) { - super.visitTypeDefinition(grTypeDefinition); - if (!(grTypeDefinition instanceof GrAnnotationTypeDefinition)) { - return; - } - final GrAnnotationTypeDefinition aClass = (GrAnnotationTypeDefinition) grTypeDefinition; + @Override + public void visitAnnotationTypeDefinition(GrAnnotationTypeDefinition aClass) { + super.visitAnnotationTypeDefinition(aClass); final String name = aClass.getName(); if (name == null) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyClassNamingConventionInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyClassNamingConventionInspection.java index 66937b0ed39c..cc48722ff817 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyClassNamingConventionInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyClassNamingConventionInspection.java @@ -20,7 +20,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor; import org.jetbrains.plugins.groovy.codeInspection.GroovyFix; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrClassDefinition; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; public class GroovyClassNamingConventionInspection extends ConventionInspection { @@ -32,7 +31,7 @@ public class GroovyClassNamingConventionInspection extends ConventionInspection return "Class naming convention"; } - protected GroovyFix buildFix(PsiElement location) { + protected GroovyFix buildFix(@NotNull PsiElement location) { return new RenameFix(); } @@ -63,26 +62,23 @@ public class GroovyClassNamingConventionInspection extends ConventionInspection return DEFAULT_MAX_LENGTH; } + @NotNull public BaseInspectionVisitor buildVisitor() { return new NamingConventionsVisitor(); } private class NamingConventionsVisitor extends BaseInspectionVisitor { - - public void visitTypeDefinition(GrTypeDefinition grTypeDefinition) { - super.visitTypeDefinition(grTypeDefinition); - if (!(grTypeDefinition instanceof GrClassDefinition)) { - return; - } - GrClassDefinition aClass = (GrClassDefinition) grTypeDefinition; - final String name = aClass.getName(); + @Override + public void visitClassDefinition(GrClassDefinition classDefinition) { + super.visitClassDefinition(classDefinition); + final String name = classDefinition.getName(); if (name == null) { return; } if (isValid(name)) { return; } - registerClassError(aClass, name); + registerClassError(classDefinition, name); } } }
\ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyEnumerationNamingConventionInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyEnumerationNamingConventionInspection.java index a53d2ed40bad..15aa51b20b1c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyEnumerationNamingConventionInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyEnumerationNamingConventionInspection.java @@ -20,7 +20,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor; import org.jetbrains.plugins.groovy.codeInspection.GroovyFix; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrEnumTypeDefinition; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; public class GroovyEnumerationNamingConventionInspection extends ConventionInspection { @@ -32,7 +31,7 @@ public class GroovyEnumerationNamingConventionInspection extends ConventionInspe return "Enumeration naming convention"; } - protected GroovyFix buildFix(PsiElement location) { + protected GroovyFix buildFix(@NotNull PsiElement location) { return new RenameFix(); } @@ -63,27 +62,24 @@ public class GroovyEnumerationNamingConventionInspection extends ConventionInspe return DEFAULT_MAX_LENGTH; } + @NotNull public BaseInspectionVisitor buildVisitor() { return new NamingConventionsVisitor(); } private class NamingConventionsVisitor extends BaseInspectionVisitor { + @Override + public void visitEnumDefinition(GrEnumTypeDefinition aClass) { + super.visitEnumDefinition(aClass); - public void visitTypeDefinition(GrTypeDefinition grTypeDefinition) { - super.visitTypeDefinition(grTypeDefinition); - if (!(grTypeDefinition instanceof GrEnumTypeDefinition)) { - return; - } - final GrEnumTypeDefinition aClass = (GrEnumTypeDefinition) grTypeDefinition; - - final String name = aClass.getName(); - if (name == null) { - return; - } - if (isValid(name)) { - return; - } - registerClassError(aClass, name); + final String name = aClass.getName(); + if (name == null) { + return; + } + if (isValid(name)) { + return; } + registerClassError(aClass, name); + } } }
\ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyInterfaceNamingConventionInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyInterfaceNamingConventionInspection.java index 832a8882c048..188995a7b9dd 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyInterfaceNamingConventionInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/naming/GroovyInterfaceNamingConventionInspection.java @@ -20,7 +20,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor; import org.jetbrains.plugins.groovy.codeInspection.GroovyFix; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrInterfaceDefinition; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; public class GroovyInterfaceNamingConventionInspection extends ConventionInspection { @@ -32,7 +31,7 @@ public class GroovyInterfaceNamingConventionInspection extends ConventionInspect return "Interface naming convention"; } - protected GroovyFix buildFix(PsiElement location) { + protected GroovyFix buildFix(@NotNull PsiElement location) { return new RenameFix(); } @@ -63,18 +62,15 @@ public class GroovyInterfaceNamingConventionInspection extends ConventionInspect return DEFAULT_MAX_LENGTH; } + @NotNull public BaseInspectionVisitor buildVisitor() { return new NamingConventionsVisitor(); } private class NamingConventionsVisitor extends BaseInspectionVisitor { - - public void visitTypeDefinition(GrTypeDefinition grTypeDefinition) { - super.visitTypeDefinition(grTypeDefinition); - if (!(grTypeDefinition instanceof GrInterfaceDefinition)) { - return; - } - final GrInterfaceDefinition aClass = (GrInterfaceDefinition) grTypeDefinition; + @Override + public void visitInterfaceDefinition(GrInterfaceDefinition aClass) { + super.visitInterfaceDefinition(aClass); final String name = aClass.getName(); if (name == null) { @@ -85,5 +81,6 @@ public class GroovyInterfaceNamingConventionInspection extends ConventionInspect } registerClassError(aClass, name); } + } }
\ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessInspection.java index 985632ad27fb..3bc8412fad48 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GrUnresolvedAccessInspection.java @@ -695,7 +695,7 @@ public class GrUnresolvedAccessInspection extends GroovySuppressableInspectionTo @Override public void unregister(@NotNull Condition<IntentionAction> condition) { if (myInfo != null) { - QuickFixAction.unregisterQuickFixAction(myInfo, condition); + myInfo.unregisterQuickFix(condition); } } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java index fa4aaf30261e..f499eb12bac9 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java @@ -691,17 +691,15 @@ public class ControlFlowUtils { @Nullable public static GrControlFlowOwner findControlFlowOwner(PsiElement place) { - if (place instanceof GrCodeBlock) { - place = place.getContext(); - } - while (true) { - assert place != null; - place = place.getContext(); - if (place == null) return null; + place = place.getContext(); + while (place != null) { if (place instanceof GrControlFlowOwner && ((GrControlFlowOwner)place).isTopControlFlowOwner()) return (GrControlFlowOwner)place; if (place instanceof GrMethod) return ((GrMethod)place).getBlock(); if (place instanceof GrClassInitializer) return ((GrClassInitializer)place).getBlock(); + + place = place.getContext(); } + return null; } /** diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/GroovyMethodInfo.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/GroovyMethodInfo.java index ae2972ea1433..3d3f6e416837 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/GroovyMethodInfo.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/GroovyMethodInfo.java @@ -4,6 +4,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.psi.*; import com.intellij.util.ArrayUtil; import com.intellij.util.PairFunction; +import com.intellij.util.SingletonInstancesCache; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -11,7 +12,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethod import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder; import org.jetbrains.plugins.groovy.refactoring.GroovyNamesUtil; -import org.jetbrains.plugins.groovy.util.ClassInstanceCache; import org.jetbrains.plugins.groovy.util.FixedValuesReferenceProvider; import java.lang.reflect.Modifier; @@ -253,7 +253,7 @@ public class GroovyMethodInfo { @NotNull public PairFunction<GrMethodCall, PsiMethod, PsiType> getReturnTypeCalculator() { if (myReturnTypeCalculatorInstance == null) { - myReturnTypeCalculatorInstance = ClassInstanceCache.getInstance(myReturnTypeCalculatorClassName, myClassLoader); + myReturnTypeCalculatorInstance = SingletonInstancesCache.getInstance(myReturnTypeCalculatorClassName, myClassLoader); } return myReturnTypeCalculatorInstance; } @@ -286,7 +286,7 @@ public class GroovyMethodInfo { public GroovyNamedArgumentProvider getNamedArgProvider() { if (myNamedArgProviderInstance == null) { - myNamedArgProviderInstance = ClassInstanceCache.getInstance(myNamedArgProviderClassName, myClassLoader); + myNamedArgProviderInstance = SingletonInstancesCache.getInstance(myNamedArgProviderClassName, myClassLoader); } return myNamedArgProviderInstance; } @@ -328,7 +328,7 @@ public class GroovyMethodInfo { private Object doGetProvider(ClassLoader classLoader) { if (myProviderClassName != null) { - return ClassInstanceCache.getInstance(myProviderClassName, classLoader); + return SingletonInstancesCache.getInstance(myProviderClassName, classLoader); } return new FixedValuesReferenceProvider(myValues); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/GroovyIntentionsBundle.properties b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/GroovyIntentionsBundle.properties index 49f772d85a2f..fc19f64b9c1f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/GroovyIntentionsBundle.properties +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/GroovyIntentionsBundle.properties @@ -202,4 +202,6 @@ flip.if.intention.family.name=Flip if statements replace.if.with.ternary.intention.name=Replace with ?: replace.if.with.ternary.intention.family.name=Replace if-statement with ternary operator gr.redundant.else.intention.name=Remove redundant 'else' keyword -gr.redundant.else.intention.family.name=Remove redundant 'else' keyword
\ No newline at end of file +gr.redundant.else.intention.family.name=Remove redundant 'else' keyword +gr.remove.explicit.type.declaration.intention.name=Remove explicit type +gr.remove.explicit.type.declaration.intention.family.name=Remove explicit type declaration
\ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/declaration/GrRemoveExplicitTypeDeclarationIntention.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/declaration/GrRemoveExplicitTypeDeclarationIntention.java new file mode 100644 index 000000000000..5f53070507ea --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/declaration/GrRemoveExplicitTypeDeclarationIntention.java @@ -0,0 +1,77 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.groovy.intentions.declaration; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.intentions.base.Intention; +import org.jetbrains.plugins.groovy.intentions.base.PsiElementPredicate; +import org.jetbrains.plugins.groovy.lang.psi.GrNamedElement; +import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; + +/** + * Created by Max Medvedev on 8/24/13 + */ +public class GrRemoveExplicitTypeDeclarationIntention extends Intention { + @Override + protected void processIntention(@NotNull PsiElement element, Project project, Editor editor) throws IncorrectOperationException { + PsiElement parent = element.getParent(); + + if (parent instanceof GrVariable) { + ((GrVariable)parent).setType(null); + } + else if (parent instanceof GrVariableDeclaration) { + ((GrVariableDeclaration)parent).setType(null); + } + else if (parent instanceof GrMethod) { + ((GrMethod)parent).setReturnType(null); + } + } + + @NotNull + @Override + protected PsiElementPredicate getElementPredicate() { + return new PsiElementPredicate() { + @Override + public boolean satisfiedBy(PsiElement element) { + PsiElement parent = element.getParent(); + if (element instanceof GrTypeElement || element instanceof GrModifierList) { + return parent instanceof GrVariableDeclaration && ((GrVariableDeclaration)parent).getTypeElementGroovy() != null || + parent instanceof GrMethod && ((GrMethod)parent).getReturnTypeElementGroovy() != null; + } + + if (parent instanceof GrNamedElement && ((GrNamedElement)parent).getNameIdentifierGroovy().equals(element)) { + if (parent instanceof GrVariable) { + return ((GrVariable)parent).getTypeElementGroovy() != null; + } + + if (parent instanceof GrMethod) { + return ((GrMethod)parent).getReturnTypeElementGroovy() != null; + } + } + + return false; + } + }; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/ReplaceAbstractClassInstanceByMapIntention.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/ReplaceAbstractClassInstanceByMapIntention.java index a145b1710246..642b64663469 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/ReplaceAbstractClassInstanceByMapIntention.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/ReplaceAbstractClassInstanceByMapIntention.java @@ -26,6 +26,7 @@ import com.intellij.psi.infos.CandidateInfo; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.config.GroovyConfigUtils; import org.jetbrains.plugins.groovy.intentions.base.Intention; import org.jetbrains.plugins.groovy.intentions.base.PsiElementPredicate; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; @@ -81,6 +82,9 @@ public class ReplaceAbstractClassInstanceByMapIntention extends Intention { if (methods.size() == 1) { final Pair<PsiMethod, GrOpenBlock> pair = methods.get(0); appendClosureTextByMethod(pair.getFirst(), buffer, pair.getSecond(), newExpr); + if (!GroovyConfigUtils.getInstance().isVersionAtLeast(psiElement, GroovyConfigUtils.GROOVY2_2)) { + buffer.append(" as ").append(iface.getQualifiedName()); + } } else { buffer.append("["); @@ -97,8 +101,9 @@ public class ReplaceAbstractClassInstanceByMapIntention extends Intention { buffer.append('\n'); } buffer.append("]"); + buffer.append(" as ").append(iface.getQualifiedName()); } - buffer.append(" as ").append(iface.getQualifiedName()); + createAndAdjustNewExpression(project, newExpr, buffer); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java index e362cba389ec..06b53a45d492 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java @@ -255,6 +255,26 @@ public abstract class GroovyElementVisitor { visitElement(typeDefinition); } + public void visitClassDefinition(GrClassDefinition classDefinition) { + visitTypeDefinition(classDefinition); + } + + public void visitEnumDefinition(GrEnumTypeDefinition enumDefinition) { + visitTypeDefinition(enumDefinition); + } + + public void visitInterfaceDefinition(GrInterfaceDefinition interfaceDefinition) { + visitTypeDefinition(interfaceDefinition); + } + + public void visitAnonymousClassDefinition(GrAnonymousClassDefinition anonymousClassDefinition) { + visitTypeDefinition(anonymousClassDefinition); + } + + public void visitAnnotationTypeDefinition(GrAnnotationTypeDefinition annotationTypeDefinition) { + visitTypeDefinition(annotationTypeDefinition); + } + public void visitExtendsClause(GrExtendsClause extendsClause) { visitElement(extendsClause); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java index 28dfd6da4506..b9a3a272d9cd 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrVariableDeclarationImpl.java @@ -96,7 +96,9 @@ public class GrVariableDeclarationImpl extends GrStubElementBase<EmptyStub> impl final GrTypeElement typeElement = getTypeElementGroovy(); if (type == null) { if (typeElement == null) return; - getModifierList().setModifierProperty(GrModifier.DEF, true); + if (getModifierList().getModifiers().length == 0) { + getModifierList().setModifierProperty(GrModifier.DEF, true); + } typeElement.delete(); return; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrBlockImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrBlockImpl.java index 74df20613b99..4331046b3ae2 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrBlockImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrBlockImpl.java @@ -16,10 +16,12 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.blocks; +import com.intellij.extapi.psi.ASTDelegatePsiElement; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.Key; import com.intellij.psi.PsiElement; import com.intellij.psi.ResolveState; +import com.intellij.psi.impl.CheckUtil; import com.intellij.psi.impl.source.tree.Factory; import com.intellij.psi.impl.source.tree.LazyParseablePsiElement; import com.intellij.psi.impl.source.tree.LeafElement; @@ -60,6 +62,17 @@ public abstract class GrBlockImpl extends LazyParseablePsiElement implements GrC } @Override + public void delete() throws IncorrectOperationException { + if (getParent() instanceof ASTDelegatePsiElement) { + CheckUtil.checkWritable(this); + ((ASTDelegatePsiElement)getParent()).deleteChildInternal(getNode()); + } + else { + getParent().deleteChildRange(this, this); + } + } + + @Override public void removeElements(PsiElement[] elements) throws IncorrectOperationException { GroovyPsiElementImpl.removeElements(this, elements); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrAnnotationTypeDefinitionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrAnnotationTypeDefinitionImpl.java index 47d849fb9cf0..16d52d00b414 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrAnnotationTypeDefinitionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrAnnotationTypeDefinitionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,10 +19,11 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.typedef; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiClassType; import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; +import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnnotationTypeDefinition; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.stubs.GrTypeDefinitionStub; -import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; /** * @author Dmitry.Krasilschikov @@ -61,4 +62,9 @@ public class GrAnnotationTypeDefinitionImpl extends GrTypeDefinitionImpl impleme private PsiClassType createAnnotationType() { return TypesUtil.createTypeByFQClassName("java.lang.annotation.Annotation", this); } + + @Override + public void accept(GroovyElementVisitor visitor) { + visitor.visitAnnotationTypeDefinition(this); + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrAnonymousClassDefinitionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrAnonymousClassDefinitionImpl.java index 495603bccbb3..aa7876a19db6 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrAnonymousClassDefinitionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrAnonymousClassDefinitionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; +import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition; @@ -250,4 +251,10 @@ public class GrAnonymousClassDefinitionImpl extends GrTypeDefinitionImpl impleme public PsiIdentifier getNameIdentifier() { return null; } + + @Override + public void accept(GroovyElementVisitor visitor) { + visitor.visitAnonymousClassDefinition(this); + } + } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrClassDefinitionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrClassDefinitionImpl.java index cafc77953e33..2e0b3d969606 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrClassDefinitionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrClassDefinitionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.typedef; import com.intellij.lang.ASTNode; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; +import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrClassDefinition; import org.jetbrains.plugins.groovy.lang.psi.stubs.GrTypeDefinitionStub; @@ -39,4 +40,9 @@ public class GrClassDefinitionImpl extends GrTypeDefinitionImpl implements GrCla public String toString() { return "Class definition"; } + + @Override + public void accept(GroovyElementVisitor visitor) { + visitor.visitClassDefinition(this); + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrEnumTypeDefinitionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrEnumTypeDefinitionImpl.java index 42308ec1959b..56f4ffdedb38 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrEnumTypeDefinitionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrEnumTypeDefinitionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,6 +31,7 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; +import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrEnumDefinitionBody; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrEnumTypeDefinition; @@ -175,4 +176,9 @@ public class GrEnumTypeDefinitionImpl extends GrTypeDefinitionImpl implements Gr if (enumDefinitionBody != null) return enumDefinitionBody.getEnumConstantList(); return null; } + + @Override + public void accept(GroovyElementVisitor visitor) { + visitor.visitEnumDefinition(this); + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrInterfaceDefinitionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrInterfaceDefinitionImpl.java index d4fa5a864f22..d093babb670b 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrInterfaceDefinitionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrInterfaceDefinitionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,11 +17,11 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.typedef; import com.intellij.lang.ASTNode; -import com.intellij.psi.stubs.IStubElementType; import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; +import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrInterfaceDefinition; import org.jetbrains.plugins.groovy.lang.psi.stubs.GrTypeDefinitionStub; -import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; /** * @author Dmitry.Krasilschikov @@ -44,4 +44,9 @@ public class GrInterfaceDefinitionImpl extends GrTypeDefinitionImpl implements G public boolean isInterface() { return true; } + + @Override + public void accept(GroovyElementVisitor visitor) { + visitor.visitInterfaceDefinition(this); + } }
\ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GdkMethodUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GdkMethodUtil.java index 74443e44012d..2f757a89ebf4 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GdkMethodUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GdkMethodUtil.java @@ -55,6 +55,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrRefer import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrGdkMethodImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder; +import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; import org.jetbrains.plugins.groovy.lang.resolve.noncode.MixinMemberContributor; import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint; import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor; @@ -226,7 +227,7 @@ public class GdkMethodUtil { final DelegatingScopeProcessor delegate = new MixinMemberContributor.MixinProcessor(processor, subjectType, qualifier); for (GrMethod method : methods) { - delegate.execute(method, ResolveState.initial()); + ResolveUtil.processElement(delegate, method, state); } } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/LocalVarAnalyzer.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/LocalVarAnalyzer.java index c130ec26f8fd..edb9c901c095 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/LocalVarAnalyzer.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/LocalVarAnalyzer.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/GrIntroduceHandlerBase.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/GrIntroduceHandlerBase.java index bed860551f03..83713e9ddb0c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/GrIntroduceHandlerBase.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/GrIntroduceHandlerBase.java @@ -77,7 +77,7 @@ import static org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.skipParentheses /** * @author Maxim.Medvedev */ -public abstract class GrIntroduceHandlerBase<Settings extends GrIntroduceSettings> implements RefactoringActionHandler { +public abstract class GrIntroduceHandlerBase<Settings extends GrIntroduceSettings, Scope extends PsiElement> implements RefactoringActionHandler { public static final Function<GrExpression, String> GR_EXPRESSION_RENDERER = new Function<GrExpression, String>() { @Override public String fun(@NotNull GrExpression expr) { @@ -124,7 +124,7 @@ public abstract class GrIntroduceHandlerBase<Settings extends GrIntroduceSetting protected abstract String getHelpID(); @NotNull - protected abstract PsiElement findScope(GrExpression expression, GrVariable variable, StringPartInfo stringPart); + protected abstract Scope[] findPossibleScopes(GrExpression expression, GrVariable variable, StringPartInfo stringPart, Editor editor); protected abstract void checkExpression(@NotNull GrExpression selectedExpr) throws GrRefactoringError; @@ -276,14 +276,41 @@ public abstract class GrIntroduceHandlerBase<Settings extends GrIntroduceSetting // Does nothing } - @NotNull - public GrIntroduceContext getContext(@NotNull Project project, - @NotNull Editor editor, - @Nullable GrExpression expression, - @Nullable GrVariable variable, - @Nullable StringPartInfo stringPart) { - final PsiElement scope = findScope(expression, variable, stringPart); + public void getContextAndInvoke(@NotNull final Project project, + @NotNull final Editor editor, + @Nullable final GrExpression expression, + @Nullable final GrVariable variable, + @Nullable final StringPartInfo stringPart) { + final Scope[] scopes = findPossibleScopes(expression, variable, stringPart, editor); + + Pass<Scope> callback = new Pass<Scope>() { + @Override + public void pass(Scope scope) { + GrIntroduceContext context = getContext(project, editor, expression, variable, stringPart, scope); + invokeImpl(project, context, editor); + } + }; + + if (scopes.length == 0) { + CommonRefactoringUtil.showErrorHint(project, editor, RefactoringBundle.getCannotRefactorMessage( getRefactoringName() + "is not available in current scope"), + getRefactoringName(), getHelpID()); + } + else if (scopes.length == 1) { + callback.pass(scopes[0]); + } + else { + showScopeChooser(scopes, callback, editor); + } + } + protected abstract void showScopeChooser(Scope[] scopes, Pass<Scope> callback, Editor editor); + + public GrIntroduceContext getContext(@NotNull Project project, + @NotNull Editor editor, + @Nullable GrExpression expression, + @Nullable GrVariable variable, + @Nullable StringPartInfo stringPart, + @NotNull PsiElement scope) { if (variable != null) { final List<PsiElement> list = Collections.synchronizedList(new ArrayList<PsiElement>()); ReferencesSearch.search(variable, new LocalSearchScope(scope)).forEach(new Processor<PsiReference>() { @@ -309,42 +336,8 @@ public abstract class GrIntroduceHandlerBase<Settings extends GrIntroduceSetting } } - @NotNull - protected PsiElement[] findOccurrences(@NotNull GrExpression expression, @NotNull PsiElement scope) { - final PsiElement[] occurrences = GroovyRefactoringUtil.getExpressionOccurrences(PsiUtil.skipParentheses(expression, false), scope); - if (occurrences == null || occurrences.length == 0) { - throw new GrRefactoringError(GroovyRefactoringBundle.message("no.occurrences.found")); - } - return occurrences; - } - - private boolean invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull PsiFile file, int startOffset, int endOffset) { + private boolean invokeImpl(final Project project, final GrIntroduceContext context, final Editor editor) { try { - PsiDocumentManager.getInstance(project).commitAllDocuments(); - if (!(file instanceof GroovyFileBase)) { - throw new GrRefactoringError(GroovyRefactoringBundle.message("only.in.groovy.files")); - } - if (!CommonRefactoringUtil.checkReadOnlyStatus(project, file)) { - throw new GrRefactoringError(RefactoringBundle.message("readonly.occurences.found")); - } - - GrExpression selectedExpr = findExpression(file, startOffset, endOffset); - final GrVariable variable = findVariable(file, startOffset, endOffset); - final StringPartInfo stringPart = StringPartInfo.findStringPart(file, startOffset, endOffset); - if (variable != null) { - checkVariable(variable); - } - else if (selectedExpr != null) { - checkExpression(selectedExpr); - } - else if (stringPart != null) { - checkStringLiteral(stringPart); - } - else { - throw new GrRefactoringError(null); - } - - final GrIntroduceContext context = getContext(project, editor, selectedExpr, variable, stringPart); if (!CommonRefactoringUtil.checkReadOnlyStatus(project, context.getOccurrences())) { return false; } @@ -370,15 +363,21 @@ public abstract class GrIntroduceHandlerBase<Settings extends GrIntroduceSetting RangeMarker stringPartRangeMarker = createRange(document, context.getStringPart()); RangeMarker varRangeMarker = createRange(document, context.getVar()); - GrVariable var = ApplicationManager.getApplication().runWriteAction(new Computable<GrVariable>() { - @Override - public GrVariable compute() { - return runRefactoring(context, settings); - } - }); + SmartPsiElementPointer<GrVariable> pointer = + ApplicationManager.getApplication().runWriteAction(new Computable<SmartPsiElementPointer<GrVariable>>() { + @Override + public SmartPsiElementPointer<GrVariable> compute() { + GrVariable var = runRefactoring(context, settings); + return var != null + ? SmartPointerManager.getInstance(context.getProject()).createSmartPsiElementPointer(var) + : null; + } + }); + GrVariable var = pointer != null ? pointer.getElement() : null; if (isInplace && var != null) { - GrInplaceIntroducer introducer = getIntroducer(var, context, settings, occurrences, varRangeMarker, expressionRangeMarker, stringPartRangeMarker); + GrInplaceIntroducer introducer = + getIntroducer(var, context, settings, occurrences, varRangeMarker, expressionRangeMarker, stringPartRangeMarker); PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument()); introducer.performInplaceRefactoring(introducer.suggestNames(context)); } @@ -411,11 +410,53 @@ public abstract class GrIntroduceHandlerBase<Settings extends GrIntroduceSetting return true; } catch (GrRefactoringError e) { - CommonRefactoringUtil.showErrorHint(project, editor, RefactoringBundle.getCannotRefactorMessage(e.getMessage()), getRefactoringName(), getHelpID()); + CommonRefactoringUtil + .showErrorHint(project, editor, RefactoringBundle.getCannotRefactorMessage(e.getMessage()), getRefactoringName(), getHelpID()); return false; } } + @NotNull + protected PsiElement[] findOccurrences(@NotNull GrExpression expression, @NotNull PsiElement scope) { + final PsiElement[] occurrences = GroovyRefactoringUtil.getExpressionOccurrences(skipParentheses(expression, false), scope); + if (occurrences == null || occurrences.length == 0) { + throw new GrRefactoringError(GroovyRefactoringBundle.message("no.occurrences.found")); + } + return occurrences; + } + + private void invoke(@NotNull final Project project, + @NotNull final Editor editor, + @NotNull PsiFile file, + int startOffset, + int endOffset) { + PsiDocumentManager.getInstance(project).commitAllDocuments(); + if (!(file instanceof GroovyFileBase)) { + throw new GrRefactoringError(GroovyRefactoringBundle.message("only.in.groovy.files")); + } + if (!CommonRefactoringUtil.checkReadOnlyStatus(project, file)) { + throw new GrRefactoringError(RefactoringBundle.message("readonly.occurences.found")); + } + + GrExpression selectedExpr = findExpression(file, startOffset, endOffset); + final GrVariable variable = findVariable(file, startOffset, endOffset); + final StringPartInfo stringPart = StringPartInfo.findStringPart(file, startOffset, endOffset); + if (variable != null) { + checkVariable(variable); + } + else if (selectedExpr != null) { + checkExpression(selectedExpr); + } + else if (stringPart != null) { + checkStringLiteral(stringPart); + } + else { + throw new GrRefactoringError(null); + } + + getContextAndInvoke(project, editor, selectedExpr, variable, stringPart); + } + private static RangeMarker createRange(Document document, StringPartInfo part) { if (part == null) { return null; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/constant/GrIntroduceConstantHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/constant/GrIntroduceConstantHandler.java index 357fa871c063..05bd5566edb9 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/constant/GrIntroduceConstantHandler.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/constant/GrIntroduceConstantHandler.java @@ -15,7 +15,9 @@ */ package org.jetbrains.plugins.groovy.refactoring.introduce.constant; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.RangeMarker; +import com.intellij.openapi.util.Pass; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.HelpID; @@ -41,7 +43,7 @@ import java.util.Map; /** * @author Maxim.Medvedev */ -public class GrIntroduceConstantHandler extends GrIntroduceHandlerBase<GrIntroduceConstantSettings> { +public class GrIntroduceConstantHandler extends GrIntroduceHandlerBase<GrIntroduceConstantSettings, PsiElement> { public static final String REFACTORING_NAME = "Introduce Constant"; @NotNull @@ -58,9 +60,9 @@ public class GrIntroduceConstantHandler extends GrIntroduceHandlerBase<GrIntrodu @NotNull @Override - protected PsiElement findScope(GrExpression expression, GrVariable variable, StringPartInfo stringPart) { + public PsiElement[] findPossibleScopes(GrExpression expression, GrVariable variable, StringPartInfo stringPart, Editor editor) { final PsiElement place = getCurrentPlace(expression, variable, stringPart); - return place.getContainingFile(); + return new PsiFile[]{place.getContainingFile()}; } @Override @@ -135,6 +137,11 @@ public class GrIntroduceConstantHandler extends GrIntroduceHandlerBase<GrIntrodu } @Override + protected void showScopeChooser(PsiElement[] scopes, Pass<PsiElement> callback, Editor editor) { + //todo do nothing right now + } + + @Override protected boolean isInplace(GrIntroduceContext context) { return false; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/field/GrInplaceIntroduceFieldPanel.form b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/field/GrInplaceIntroduceFieldPanel.form index 3ad6c01fffa4..96f32e533680 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/field/GrInplaceIntroduceFieldPanel.form +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/field/GrInplaceIntroduceFieldPanel.form @@ -21,6 +21,7 @@ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/> </constraints> <properties> + <labelFor value="58f26"/> <text value="&Initialize in:"/> </properties> </component> diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/field/GrIntroduceFieldHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/field/GrIntroduceFieldHandler.java index 1081b611b717..27878e97cecf 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/field/GrIntroduceFieldHandler.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/field/GrIntroduceFieldHandler.java @@ -16,17 +16,21 @@ package org.jetbrains.plugins.groovy.refactoring.introduce.field; import com.intellij.codeInsight.TestFrameworks; +import com.intellij.codeInsight.navigation.NavigationUtil; +import com.intellij.ide.util.PsiClassListCellRenderer; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.RangeMarker; +import com.intellij.openapi.util.Pass; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiModifier; import com.intellij.psi.PsiType; +import com.intellij.psi.search.PsiElementProcessor; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.HelpID; import com.intellij.refactoring.introduce.inplace.OccurrencesChooser; import com.intellij.refactoring.introduceField.IntroduceFieldHandler; -import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,7 +55,7 @@ import java.util.List; /** * @author Maxim.Medvedev */ -public class GrIntroduceFieldHandler extends GrIntroduceHandlerBase<GrIntroduceFieldSettings> { +public class GrIntroduceFieldHandler extends GrIntroduceHandlerBase<GrIntroduceFieldSettings, PsiClass> { @NotNull @Override @@ -67,9 +71,23 @@ public class GrIntroduceFieldHandler extends GrIntroduceHandlerBase<GrIntroduceF @NotNull @Override - protected PsiClass findScope(GrExpression expression, GrVariable variable, StringPartInfo partInfo) { + protected PsiClass[] findPossibleScopes(GrExpression expression, + GrVariable variable, + StringPartInfo partInfo, + Editor editor) { PsiElement place = getCurrentPlace(expression, variable, partInfo); - return ObjectUtils.assertNotNull(PsiUtil.getContextClass(place)); + PsiClass aClass = PsiUtil.getContextClass(place); + if (aClass instanceof GroovyScriptClass) { + return new PsiClass[]{aClass}; + } + else { + List<PsiClass> result = ContainerUtil.newArrayList(aClass); + while (aClass != null) { + aClass = PsiTreeUtil.getParentOfType(aClass, PsiClass.class); + ContainerUtil.addIfNotNull(result, aClass); + } + return result.toArray(new PsiClass[result.size()]); + } } @Override @@ -233,6 +251,19 @@ public class GrIntroduceFieldHandler extends GrIntroduceHandlerBase<GrIntroduceF }; } + @Override + protected void showScopeChooser(PsiClass[] scopes, final Pass<PsiClass> callback, Editor editor) { + PsiElementProcessor<PsiClass> processor = new PsiElementProcessor<PsiClass>() { + @Override + public boolean execute(@NotNull PsiClass element) { + callback.pass(element); + return false; + } + }; + + NavigationUtil.getPsiElementPopup(scopes, new PsiClassListCellRenderer(), "Choose class to introduce field", processor).showInBestPositionFor(editor); + } + @NotNull @Override protected PsiElement[] findOccurrences(@NotNull GrExpression expression, @NotNull PsiElement scope) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/variable/GrIntroduceLocalVariableProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/variable/GrIntroduceLocalVariableProcessor.java index ff941bec3e06..3e3f6888d339 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/variable/GrIntroduceLocalVariableProcessor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/variable/GrIntroduceLocalVariableProcessor.java @@ -75,11 +75,12 @@ public class GrIntroduceLocalVariableProcessor { int expressionIndex = ArrayUtilRt.find(myOccurrences, myExpression); final PsiElement[] replaced = processOccurrences(); - GrStatement anchor = getAnchor(replaced); + PsiElement replacedExpression = replaced[expressionIndex]; + GrStatement anchor = getAnchor(replaced, replacedExpression); RefactoringUtil.highlightAllOccurrences(myContext.getProject(), replaced, myContext.getEditor()); - return insertVariableDefinition(declaration, anchor, replaced[expressionIndex]); + return insertVariableDefinition(declaration, anchor, replacedExpression); } private void refreshPositionMarker(PsiElement e) { @@ -206,8 +207,8 @@ public class GrIntroduceLocalVariableProcessor { } @NotNull - private GrStatement getAnchor(PsiElement[] replaced) { - PsiElement anchor = GrIntroduceHandlerBase.findAnchor(replaced, myContext.getScope()); + private GrStatement getAnchor(PsiElement[] replaced, PsiElement replacedExpression) { + PsiElement anchor = GrIntroduceHandlerBase.findAnchor(replaced, GroovyRefactoringUtil.getEnclosingContainer(replacedExpression)); if (!(anchor instanceof GrStatement)) { StringBuilder error = new StringBuilder("scope:"); error.append(myContext.getScope()); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/variable/GrIntroduceVariableHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/variable/GrIntroduceVariableHandler.java index 5ce7a0985305..c27df0697130 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/variable/GrIntroduceVariableHandler.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/variable/GrIntroduceVariableHandler.java @@ -16,7 +16,9 @@ package org.jetbrains.plugins.groovy.refactoring.introduce.variable; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.RangeMarker; +import com.intellij.openapi.util.Pass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiModifier; import com.intellij.psi.PsiType; @@ -25,8 +27,9 @@ import com.intellij.refactoring.introduce.inplace.OccurrencesChooser; import com.intellij.refactoring.util.CanonicalTypes; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; +import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner; import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; -import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; @@ -46,19 +49,20 @@ import java.util.List; /** * @author ilyas */ -public class GrIntroduceVariableHandler extends GrIntroduceHandlerBase<GroovyIntroduceVariableSettings> { +public class GrIntroduceVariableHandler extends GrIntroduceHandlerBase<GroovyIntroduceVariableSettings, GrControlFlowOwner> { public static final String DUMMY_NAME = "________________xxx_________________"; protected static final String REFACTORING_NAME = GroovyRefactoringBundle.message("introduce.variable.title"); private RangeMarker myPosition = null; @NotNull @Override - protected PsiElement findScope(GrExpression selectedExpr, GrVariable variable, StringPartInfo stringPartInfo) { + protected GrControlFlowOwner[] findPossibleScopes(GrExpression selectedExpr, + GrVariable variable, + StringPartInfo stringPartInfo, + Editor editor) { // Get container element - final PsiElement scope = stringPartInfo != null - ? GroovyRefactoringUtil.getEnclosingContainer(stringPartInfo.getLiteral()) - : GroovyRefactoringUtil.getEnclosingContainer(selectedExpr); - if (scope == null || !(scope instanceof GroovyPsiElement)) { + final GrControlFlowOwner scope = ControlFlowUtils.findControlFlowOwner(stringPartInfo != null ? stringPartInfo.getLiteral() : selectedExpr); + if (scope == null) { throw new GrRefactoringError( GroovyRefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME)); } @@ -66,7 +70,7 @@ public class GrIntroduceVariableHandler extends GrIntroduceHandlerBase<GroovyInt throw new GrRefactoringError( GroovyRefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME)); } - return scope; + return new GrControlFlowOwner[]{scope}; } protected void checkExpression(@NotNull GrExpression selectedExpr) { @@ -187,6 +191,11 @@ public class GrIntroduceVariableHandler extends GrIntroduceHandlerBase<GroovyInt }; } + @Override + protected void showScopeChooser(GrControlFlowOwner[] scopes, Pass<GrControlFlowOwner> callback, Editor editor) { + //todo do nothing right now + } + @NotNull private static GrVariableDeclaration generateDeclaration(@NotNull GrIntroduceContext context, @NotNull GroovyIntroduceVariableSettings settings) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/testIntegration/GroovyTestFramework.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/testIntegration/GroovyTestFramework.java index 3cdabe38a8e0..acac929bf281 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/testIntegration/GroovyTestFramework.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/testIntegration/GroovyTestFramework.java @@ -51,12 +51,14 @@ public class GroovyTestFramework extends JavaTestFramework { @Override protected boolean isTestClass(PsiClass clazz, boolean canBePotential) { return clazz.getLanguage() == GroovyFileType.GROOVY_LANGUAGE && - JUnitUtil.isTestClass(clazz) && + //JUnitUtil.isTestClass(clazz) && InheritanceUtil.isInheritor(clazz, GroovyCommonClassNames.GROOVY_UTIL_TEST_CASE); } @Override protected PsiMethod findSetUpMethod(@NotNull PsiClass clazz) { + if (!isTestClass(clazz, false)) return null; + for (PsiMethod method : clazz.getMethods()) { if (method.getName().equals("setUp")) return method; } @@ -65,6 +67,8 @@ public class GroovyTestFramework extends JavaTestFramework { @Override protected PsiMethod findTearDownMethod(@NotNull PsiClass clazz) { + if (!isTestClass(clazz, false)) return null; + for (PsiMethod method : clazz.getMethods()) { if (method.getName().equals("tearDown")) return method; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/util/ClassInstanceCache.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/util/ClassInstanceCache.java deleted file mode 100644 index 7a9f0554e661..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/util/ClassInstanceCache.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.jetbrains.plugins.groovy.util; - -import com.intellij.util.containers.ConcurrentHashMap; -import org.jetbrains.annotations.NotNull; - -/** - * @author Sergey Evdokimov - */ -public class ClassInstanceCache { - - private static final ConcurrentHashMap<String, Object> CACHE = new ConcurrentHashMap<String, Object>(); - - private ClassInstanceCache() { - } - - @SuppressWarnings("unchecked") - public static <T> T getInstance(@NotNull String className, ClassLoader classLoader) { - Object res = CACHE.get(className); - if (res == null) { - try { - res = classLoader.loadClass(className).newInstance(); - } - catch (Exception e) { - throw new RuntimeException(e); - } - - Object oldValue = CACHE.putIfAbsent(className, res); - if (oldValue != null) { - res = oldValue; - } - } - - return (T)res; - } -} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyUnwrapTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyUnwrapTest.groovy index 867c34341749..a69992207dc9 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyUnwrapTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyUnwrapTest.groovy @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.jetbrains.plugins.groovy.lang import com.intellij.codeInsight.unwrap.UnwrapHandler @@ -51,13 +66,11 @@ for(int i = 0; i < 10; i++) { } public void testBraces() throws Exception { - assertUnwrapped(""" + assertUnwrapped("""\ <caret>{ def x = 1 } -""", """ -def x = 1 -"""); +""", "def x = 1"); } public void testUnwrapParameterUnderArgumentList() throws Exception { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/GroovyResolveTestCase.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/GroovyResolveTestCase.groovy index 6407c317daae..c481df10fa97 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/GroovyResolveTestCase.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/GroovyResolveTestCase.groovy @@ -95,7 +95,7 @@ public abstract class GroovyResolveTestCase extends LightGroovyTestCase { final ref = configureByText(text) assertNotNull(ref) final resolved = ref.resolve() - assertInstanceOf(resolved, type) + if (type != null) assertInstanceOf(resolved, type) return resolved } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy index 8dc669709410..f6ee5f0a471d 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy @@ -1431,6 +1431,30 @@ class _a { ''', PsiMethod) } + void testRuntimeMixin22() { + assertNull resolveByText('''\ +class ReentrantLock {} + +ReentrantLock.metaClass.withLock = { nestedCode -> } + +new ReentrantLock().withLock { + fo<caret>o(3) +} +''') + } + + void testRuntimeMixin23() { + assertNotNull resolveByText('''\ +class ReentrantLock {} + +ReentrantLock.metaClass.withLock = { nestedCode -> } + +new ReentrantLock().withLock { + withL<caret>ock(2) +} +''') + } + void testRunnableVsCallable() { final PsiMethod method = resolveByText('''\ import java.util.concurrent.Callable diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/IntroduceConstantTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/IntroduceConstantTest.java index f51e02a507bd..77bc4a185ac8 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/IntroduceConstantTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/IntroduceConstantTest.java @@ -18,6 +18,7 @@ package org.jetbrains.plugins.groovy.refactoring; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiModifier; import com.intellij.psi.PsiType; import com.intellij.psi.impl.source.PostprocessReformattingAspect; @@ -86,7 +87,8 @@ public class IntroduceConstantTest extends LightCodeInsightFixtureTestCase { final GrExpression expression = findExpression(); final GrVariable variable = findVariable(); final StringPartInfo stringPart = findStringPart(); - final GrIntroduceContext context = handler.getContext(getProject(), editor, expression, variable, stringPart); + PsiElement[] scopes = handler.findPossibleScopes(expression, variable, stringPart, editor); + final GrIntroduceContext context = handler.getContext(getProject(), editor, expression, variable, stringPart, scopes[0]); PsiClass targetClass; if (targetClassName == null) { diff --git a/plugins/groovy/testdata/groovy/refactoring/introduceVariable/f2.test b/plugins/groovy/testdata/groovy/refactoring/introduceVariable/f2.test index 7e89dbf873c8..f1a15b464d1e 100644 --- a/plugins/groovy/testdata/groovy/refactoring/introduceVariable/f2.test +++ b/plugins/groovy/testdata/groovy/refactoring/introduceVariable/f2.test @@ -4,5 +4,5 @@ for (i in 1..<all>boo<end>){ ----- def preved = boo for (i in 1..preved<caret>){ - def foo = boo + def foo = preved }
\ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/refactoring/introduceVariable/loop7.test b/plugins/groovy/testdata/groovy/refactoring/introduceVariable/loop7.test index ca3771e36a0c..e8869c67abf5 100644 --- a/plugins/groovy/testdata/groovy/refactoring/introduceVariable/loop7.test +++ b/plugins/groovy/testdata/groovy/refactoring/introduceVariable/loop7.test @@ -4,5 +4,5 @@ while (<all>true<end>) { ----- def preved = true while (preved<caret>) { - true + preved }
\ No newline at end of file diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgChangeProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgChangeProvider.java index 371fc70979b5..9c686055db6b 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgChangeProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgChangeProvider.java @@ -221,10 +221,11 @@ public class HgChangeProvider implements ChangeProvider { // The original file exists so this is a duplication of the file. // Don't create the before ContentRevision or IDEA will think // this was a rename. + //todo: fix this unexpected status behavior (sometimes added status instead of copied, and copied instead of renamed ) processChange( null, HgCurrentContentRevision.create(afterFile, currentNumber), - HgChangeProvider.COPIED, + FileStatus.ADDED, builder, vcsKey ); diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/descriptors/JavaFxPropertyAttributeDescriptor.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/descriptors/JavaFxPropertyAttributeDescriptor.java index 60d7a433d421..37488f345330 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/descriptors/JavaFxPropertyAttributeDescriptor.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/descriptors/JavaFxPropertyAttributeDescriptor.java @@ -8,6 +8,7 @@ import com.intellij.util.ArrayUtil; import com.intellij.xml.XmlAttributeDescriptor; import com.intellij.xml.XmlElementDescriptor; import com.intellij.xml.impl.BasicXmlAttributeDescriptor; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.javaFX.fxml.FxmlConstants; import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonClassNames; @@ -209,9 +210,8 @@ public class JavaFxPropertyAttributeDescriptor extends BasicXmlAttributeDescript } @Override - public PsiReference[] getValueReferences(XmlAttributeValue value) { - String s = value.getValue(); - return s != null && !s.startsWith("${") ? super.getValueReferences(value) : PsiReference.EMPTY_ARRAY; + public PsiReference[] getValueReferences(XmlElement element, @NotNull String text) { + return !text.startsWith("${") ? super.getValueReferences(element, text) : PsiReference.EMPTY_ARRAY; } @Override diff --git a/plugins/junit/src/com/intellij/execution/junit/JUnit3Framework.java b/plugins/junit/src/com/intellij/execution/junit/JUnit3Framework.java index 2bebb05b5cff..21828ecb2afb 100644 --- a/plugins/junit/src/com/intellij/execution/junit/JUnit3Framework.java +++ b/plugins/junit/src/com/intellij/execution/junit/JUnit3Framework.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,6 +63,8 @@ public class JUnit3Framework extends JavaTestFramework { @Override @Nullable protected PsiMethod findSetUpMethod(@NotNull PsiClass clazz) { + if (!JUnitUtil.isJUnit3TestClass(clazz)) return null; + for (PsiMethod each : clazz.getMethods()) { if (each.getName().equals("setUp")) return each; } @@ -72,6 +74,8 @@ public class JUnit3Framework extends JavaTestFramework { @Override @Nullable protected PsiMethod findTearDownMethod(@NotNull PsiClass clazz) { + if (!JUnitUtil.isJUnit3TestClass(clazz)) return null; + for (PsiMethod each : clazz.getMethods()) { if (each.getName().equals("tearDown")) return each; } diff --git a/plugins/junit/src/com/intellij/execution/junit/JUnitConfiguration.java b/plugins/junit/src/com/intellij/execution/junit/JUnitConfiguration.java index 569d4b12152a..422e567f8949 100644 --- a/plugins/junit/src/com/intellij/execution/junit/JUnitConfiguration.java +++ b/plugins/junit/src/com/intellij/execution/junit/JUnitConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -492,7 +492,7 @@ public class JUnitConfiguration extends ModuleBasedConfiguration<JavaRunConfigur final String fqName = myPattern.iterator().next(); return (fqName.contains("*") ? fqName : StringUtil.getShortName(fqName)) + (size > 1 ? " and " + (size - 1) + " more" : ""); } - final String className = JavaExecutionUtil.getPresentableClassName(getMainClassName(), configurationModule); + final String className = JavaExecutionUtil.getPresentableClassName(getMainClassName()); if (TEST_METHOD.equals(TEST_OBJECT)) { return className + '.' + getMethodName(); } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/converters/MavenDependencySystemPathConverter.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/converters/MavenDependencySystemPathConverter.java index ae91be9749ef..2641d4130a45 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/converters/MavenDependencySystemPathConverter.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/converters/MavenDependencySystemPathConverter.java @@ -15,20 +15,18 @@ */ package org.jetbrains.idea.maven.dom.converters; -import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; -import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet; -import com.intellij.psi.xml.XmlElement; import com.intellij.util.xml.ConvertContext; import com.intellij.util.xml.CustomReferenceConverter; import com.intellij.util.xml.GenericDomValue; import com.intellij.util.xml.ResolvingConverter; -import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.maven.dom.references.MavenPathReferenceConverter; import java.util.Collection; import java.util.Collections; @@ -54,63 +52,12 @@ public class MavenDependencySystemPathConverter extends ResolvingConverter<PsiFi @NotNull public PsiReference[] createReferences(final GenericDomValue genericDomValue, final PsiElement element, final ConvertContext context) { - XmlElement xmlElement = genericDomValue.getXmlElement(); - - if (xmlElement != null && xmlElement.getText().contains("${")) return PsiReference.EMPTY_ARRAY; - - return createReferences(element, true); - } - - @NotNull - public static PsiReference[] createReferences(@NotNull final PsiElement psiElement, final boolean soft) { - FileReferenceSet set = new MyFileReferenceSet(psiElement, soft); - - return set.getAllReferences(); - } - - private static class MyFileReferenceSet extends FileReferenceSet { - private final boolean mySoft; - - public MyFileReferenceSet(PsiElement psiElement, boolean soft) { - super(psiElement); - mySoft = soft; - } - - @Override - public boolean isAbsolutePathReference() { - return true; - } - - @Override - protected boolean isSoft() { - return mySoft; - } - - @NotNull - @Override - public Collection<PsiFileSystemItem> getDefaultContexts() { - Collection<PsiFileSystemItem> systemItemCollection = super.getDefaultContexts(); - if (isAbsolutePathReference()) { - VirtualFile vFile = LocalFileSystem.getInstance().getRoot(); - - if (ApplicationManager.getApplication().isUnitTestMode()) { - assert vFile != null : ""; // - } - - if (vFile != null) { - final PsiDirectory directory = getElement().getManager().findDirectory(vFile); - - if (ApplicationManager.getApplication().isUnitTestMode()) { - assert directory != null : "for element: " + getElement().getText(); // - } - - if (directory != null) { - systemItemCollection = new THashSet<PsiFileSystemItem>(systemItemCollection); - systemItemCollection.add(directory); - } - } + return MavenPathReferenceConverter.createReferences(genericDomValue, element, new Condition<PsiFileSystemItem>() { + @Override + public boolean value(PsiFileSystemItem item) { + return (item instanceof PsiDirectory) || item.getName().endsWith(".jar"); } - return systemItemCollection; - } + }, true); } } + diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/converters/MavenUrlConverter.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/converters/MavenUrlConverter.java index c4f864a642ec..d7a4ca4bae4f 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/converters/MavenUrlConverter.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/converters/MavenUrlConverter.java @@ -18,10 +18,10 @@ package org.jetbrains.idea.maven.dom.converters; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; +import com.intellij.psi.impl.UrlPsiReference; import com.intellij.util.xml.ConvertContext; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.maven.dom.references.MavenUrlPsiReference; public class MavenUrlConverter extends MavenReferenceConverter<String> { @Override @@ -35,6 +35,6 @@ public class MavenUrlConverter extends MavenReferenceConverter<String> { } protected PsiReference createReference(PsiElement element, String text, TextRange range) { - return new MavenUrlPsiReference(element, text, range); + return new UrlPsiReference(element); } }
\ No newline at end of file diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/converters/repositories/MavenRepositoryConverter.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/converters/repositories/MavenRepositoryConverter.java index 4b08baf40d1d..46fa023da939 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/converters/repositories/MavenRepositoryConverter.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/converters/repositories/MavenRepositoryConverter.java @@ -18,11 +18,10 @@ package org.jetbrains.idea.maven.dom.converters.repositories; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.module.Module; -import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.ElementManipulators; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; +import com.intellij.psi.impl.UrlPsiReference; import com.intellij.util.xml.ConvertContext; import com.intellij.util.xml.GenericDomValue; import com.intellij.util.xml.ResolvingConverter; @@ -31,7 +30,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.maven.dom.converters.MavenUrlConverter; import org.jetbrains.idea.maven.dom.model.MavenDomRepositoryBase; -import org.jetbrains.idea.maven.dom.references.MavenUrlPsiReference; import java.util.Collection; import java.util.Collections; @@ -77,9 +75,7 @@ public abstract class MavenRepositoryConverter extends ResolvingConverter<String @NotNull @Override public PsiReference[] createReferences(GenericDomValue value, final PsiElement element, final ConvertContext context) { - String text = value.getStringValue(); - TextRange range = ElementManipulators.getValueTextRange(element); - return new PsiReference[]{new MavenUrlPsiReference(element, text, range) { + return new PsiReference[]{new UrlPsiReference(element) { @NotNull @Override diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPathReferenceConverter.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPathReferenceConverter.java index 56672d738b10..b136cf440f59 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPathReferenceConverter.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPathReferenceConverter.java @@ -23,6 +23,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference; import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet; +import com.intellij.util.Function; import com.intellij.util.xml.ConvertContext; import com.intellij.util.xml.DomElement; import com.intellij.util.xml.DomUtil; @@ -33,6 +34,7 @@ import org.jetbrains.idea.maven.dom.MavenPropertyResolver; import org.jetbrains.idea.maven.dom.model.MavenDomProjectModel; import java.util.Collection; +import java.util.Collections; /** * @author Sergey Evdokimov @@ -52,6 +54,12 @@ public class MavenPathReferenceConverter extends PathReferenceConverter { public static PsiReference[] createReferences(final DomElement genericDomValue, PsiElement element, @NotNull final Condition<PsiFileSystemItem> fileFilter) { + return createReferences(genericDomValue, element, fileFilter, false); + } + + public static PsiReference[] createReferences(final DomElement genericDomValue, + PsiElement element, + @NotNull final Condition<PsiFileSystemItem> fileFilter, boolean isAbsolutePath) { ElementManipulator<PsiElement> manipulator = ElementManipulators.getManipulator(element); TextRange range = manipulator.getRangeInElement(element); String text = range.substring(element.getText()); @@ -88,7 +96,19 @@ public class MavenPathReferenceConverter extends PathReferenceConverter { String resolvedText = model == null ? text : MavenPropertyResolver.resolve(text, model); if (resolvedText.equals(text)) { - super.innerResolveInContext(resolvedText, context, result, caseSensitive); + if (getIndex() == 0 && resolvedText.length() == 2 && resolvedText.charAt(1) == ':') { + // it's root on windows, e.g. "C:" + VirtualFile file = LocalFileSystem.getInstance().findFileByPath(resolvedText + '/'); + if (file != null) { + PsiDirectory psiDirectory = context.getManager().findDirectory(file); + if (psiDirectory != null) { + result.add(new PsiElementResolveResult(psiDirectory)); + } + } + } + else { + super.innerResolveInContext(resolvedText, context, result, caseSensitive); + } } else { VirtualFile contextFile = context.getVirtualFile(); @@ -117,6 +137,33 @@ public class MavenPathReferenceConverter extends PathReferenceConverter { } }; + if (isAbsolutePath) { + set.addCustomization(FileReferenceSet.DEFAULT_PATH_EVALUATOR_OPTION, new Function<PsiFile, Collection<PsiFileSystemItem>>() { + @Override + public Collection<PsiFileSystemItem> fun(PsiFile file) { + VirtualFile virtualFile = file.getVirtualFile(); + + if (virtualFile == null) { + return FileReferenceSet.ABSOLUTE_TOP_LEVEL.fun(file); + } + + while (true) { + VirtualFile parent = virtualFile.getParent(); + if (parent == null) break; + virtualFile = parent; + } + + PsiDirectory root = file.getManager().findDirectory(virtualFile); + + if (root == null) { + return FileReferenceSet.ABSOLUTE_TOP_LEVEL.fun(file); + } + + return Collections.<PsiFileSystemItem>singletonList(root); + } + }); + } + return set.getAllReferences(); } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenUrlPsiReference.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenUrlPsiReference.java deleted file mode 100644 index 12fd29d00798..000000000000 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenUrlPsiReference.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.idea.maven.dom.references; - -import com.intellij.ide.BrowserUtil; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; -import com.intellij.psi.impl.FakePsiElement; -import org.jetbrains.annotations.NotNull; - -public class MavenUrlPsiReference extends MavenPsiReference { - public MavenUrlPsiReference(PsiElement element, String text, TextRange range) { - super(element, text, range); - } - - public PsiElement resolve() { - return new FakePsiElement() { - public PsiElement getParent() { - return myElement; - } - - @Override - public String getName() { - return myText; - } - - @Override - public void navigate(boolean requestFocus) { - BrowserUtil.launchBrowser(myText); - } - }; - } - - @NotNull - public Object[] getVariants() { - return EMPTY_ARRAY; - } -}
\ No newline at end of file diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/MavenProjectsNavigatorPanel.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/MavenProjectsNavigatorPanel.java index 31380d2c4b01..69cd93efab2a 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/MavenProjectsNavigatorPanel.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/MavenProjectsNavigatorPanel.java @@ -298,7 +298,7 @@ public class MavenProjectsNavigatorPanel extends SimpleToolWindowPanel implement return false; } - manager.addManagedFiles(pomFiles); + manager.addManagedFilesOrUnignore(pomFiles); return true; } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenModelPropertiesPatcher.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenModelPropertiesPatcher.java index defe2bf99d6c..047fc823199b 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenModelPropertiesPatcher.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenModelPropertiesPatcher.java @@ -1,10 +1,10 @@ package org.jetbrains.idea.maven.plugins.api; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.SingletonInstancesCache; import org.jdom.Element; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.maven.model.MavenPlugin; -import org.jetbrains.plugins.groovy.util.ClassInstanceCache; import java.util.Collection; import java.util.List; @@ -54,7 +54,8 @@ public class MavenModelPropertiesPatcher { } if (descriptor.propertyGenerator != null) { - MavenPropertiesGenerator generator = ClassInstanceCache.getInstance(descriptor.propertyGenerator, descriptor.getLoaderForClass()); + MavenPropertiesGenerator generator = SingletonInstancesCache + .getInstance(descriptor.propertyGenerator, descriptor.getLoaderForClass()); generator.generate(modelProperties, goal, plugin, cfgElement); } } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java index 5564984a835d..678f811b0c34 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java @@ -484,6 +484,11 @@ public class MavenProjectsManager extends MavenSimpleProjectComponent addManagedFilesWithProfiles(files, Collections.<String>emptyList()); } + public void addManagedFilesOrUnignore(@NotNull List<VirtualFile> files) { + removeIgnoredFilesPaths(MavenUtil.collectPaths(files)); + addManagedFiles(files); + } + public void removeManagedFiles(@NotNull List<VirtualFile> files) { myWatcher.removeManagedFiles(files); } @@ -629,6 +634,11 @@ public class MavenProjectsManager extends MavenSimpleProjectComponent myProjectsTree.setIgnoredFilesPaths(paths); } + public void removeIgnoredFilesPaths(final Collection<String> paths) { + if (!isInitialized()) return; + myProjectsTree.removeIgnoredFilesPaths(paths); + } + public boolean getIgnoredState(@NotNull MavenProject project) { if (!isInitialized()) return false; return myProjectsTree.getIgnoredState(project); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java index 721908c4870b..81eb778b2466 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManagerWatcher.java @@ -112,7 +112,16 @@ public class MavenProjectsManagerWatcher { @Override public void moduleRemoved(Project project, Module module) { MavenProject mavenProject = myManager.findProject(module); - if (mavenProject != null) myManager.setIgnoredState(Collections.singletonList(mavenProject), true); + if (mavenProject != null && !myManager.isIgnored(mavenProject)) { + VirtualFile file = mavenProject.getFile(); + + if (myManager.isManagedFile(file) && myManager.getModules(mavenProject).isEmpty()) { + myManager.removeManagedFiles(Collections.singletonList(file)); + } + else { + myManager.setIgnoredState(Collections.singletonList(mavenProject), true); + } + } } @Override diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java index 3a39929978f0..a11d9cb13459 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java @@ -57,7 +57,7 @@ public class MavenProjectsTree { private final Lock myStructureWriteLock = myStructureLock.writeLock(); // TODO replace with sets - private volatile List<String> myManagedFilesPaths = new ArrayList<String>(); + private volatile Set<String> myManagedFilesPaths = new LinkedHashSet<String>(); private volatile List<String> myIgnoredFilesPaths = new ArrayList<String>(); private volatile List<String> myIgnoredFilesPatterns = new ArrayList<String>(); private volatile Pattern myIgnoredFilesPatternsCache; @@ -91,7 +91,7 @@ public class MavenProjectsTree { try { try { if (!STORAGE_VERSION.equals(in.readUTF())) return null; - result.myManagedFilesPaths = readCollection(in, new ArrayList<String>()); + result.myManagedFilesPaths = readCollection(in, new LinkedHashSet<String>()); result.myIgnoredFilesPaths = readCollection(in, new ArrayList<String>()); result.myIgnoredFilesPatterns = readCollection(in, new ArrayList<String>()); result.myExplicitProfiles = readCollection(in, new THashSet<String>()); @@ -190,7 +190,7 @@ public class MavenProjectsTree { public void resetManagedFilesPathsAndProfiles(List<String> paths, Collection<String> profiles) { synchronized (myStateLock) { - myManagedFilesPaths = new ArrayList<String>(paths); + myManagedFilesPaths = new LinkedHashSet<String>(paths); } setExplicitProfiles(profiles); } @@ -243,6 +243,14 @@ public class MavenProjectsTree { }); } + public void removeIgnoredFilesPaths(final Collection<String> paths) { + doChangeIgnoreStatus(new Runnable() { + public void run() { + myIgnoredFilesPaths.removeAll(paths); + } + }); + } + public boolean getIgnoredState(MavenProject project) { synchronized (myStateLock) { return myIgnoredFilesPaths.contains(project.getPath()); @@ -672,10 +680,12 @@ public class MavenProjectsTree { } public boolean isManagedFile(String path) { - for (String each : getManagedFilesPaths()) { - if (FileUtil.pathsEqual(each, path)) return true; + synchronized (myStateLock) { + for (String each : myManagedFilesPaths) { + if (FileUtil.pathsEqual(each, path)) return true; + } + return false; } - return false; } public boolean isPotentialProject(String path) { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/actions/AddFileAsMavenProjectAction.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/actions/AddFileAsMavenProjectAction.java index a7d686344e36..c2980a0bfc15 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/actions/AddFileAsMavenProjectAction.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/actions/AddFileAsMavenProjectAction.java @@ -31,7 +31,7 @@ public class AddFileAsMavenProjectAction extends MavenAction { public void actionPerformed(AnActionEvent e) { final DataContext context = e.getDataContext(); MavenProjectsManager manager = MavenActionUtil.getProjectsManager(context); - manager.addManagedFiles(Collections.singletonList(getSelectedFile(context))); + manager.addManagedFilesOrUnignore(Collections.singletonList(getSelectedFile(context))); } @Override diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/actions/AddManagedFilesAction.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/actions/AddManagedFilesAction.java index f4b12b8337f4..4ebf81b1aaca 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/actions/AddManagedFilesAction.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/actions/AddManagedFilesAction.java @@ -50,6 +50,6 @@ public class AddManagedFilesAction extends MavenAction { VirtualFile[] files = FileChooser.chooseFiles(singlePomSelection, project, fileToSelect); if (files.length == 0) return; - manager.addManagedFiles(Arrays.asList(files)); + manager.addManagedFilesOrUnignore(Arrays.asList(files)); } } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenFrameworkSupportProvider.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenFrameworkSupportProvider.java index 01ea1fe3f5de..ec8b82c1bb42 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenFrameworkSupportProvider.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenFrameworkSupportProvider.java @@ -66,7 +66,7 @@ public class MavenFrameworkSupportProvider extends FrameworkSupportProvider { VirtualFile existingPom = root.findChild(MavenConstants.POM_XML); if (existingPom != null) { - MavenProjectsManager.getInstance(module.getProject()).addManagedFiles(Collections.singletonList(existingPom)); + MavenProjectsManager.getInstance(module.getProject()).addManagedFilesOrUnignore(Collections.singletonList(existingPom)); } else { prepareProjectStructure(model, root); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenModuleBuilderHelper.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenModuleBuilderHelper.java index f951597aefaf..7494babcb7b7 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenModuleBuilderHelper.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenModuleBuilderHelper.java @@ -117,7 +117,7 @@ public class MavenModuleBuilderHelper { if (myAggregatorProject == null) { MavenProjectsManager manager = MavenProjectsManager.getInstance(project); - manager.addManagedFiles(Collections.singletonList(pom)); + manager.addManagedFilesOrUnignore(Collections.singletonList(pom)); } if (myArchetype == null) { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenProjectBuilder.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenProjectBuilder.java index a516632a38ca..53a486fb47ba 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenProjectBuilder.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenProjectBuilder.java @@ -126,6 +126,9 @@ public class MavenProjectBuilder extends ProjectImportBuilder<MavenProject> { } MavenProjectsManager manager = MavenProjectsManager.getInstance(project); + + manager.setIgnoredState(getParameters().mySelectedProjects, false); + manager.addManagedFilesWithProfiles(MavenUtil.collectFiles(getParameters().mySelectedProjects), selectedProfiles); manager.waitForReadingCompletion(); diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenDependencyCompletionAndResolutionTest.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenDependencyCompletionAndResolutionTest.java index 61858813c2ee..2b257d97e00a 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenDependencyCompletionAndResolutionTest.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenDependencyCompletionAndResolutionTest.java @@ -31,6 +31,7 @@ import org.jetbrains.idea.maven.dom.intentions.ChooseFileIntentionAction; import org.jetbrains.idea.maven.dom.model.MavenDomDependency; import org.jetbrains.idea.maven.dom.model.MavenDomProjectModel; +import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -661,6 +662,30 @@ public class MavenDependencyCompletionAndResolutionTest extends MavenDomWithIndi checkHighlighting(); } + public void testCompletionSystemScopeDependenciesWithProperties() throws Throwable { + String libPath = myIndicesFixture.getRepositoryHelper().getTestDataPath("local1/junit/junit/4.0/junit-4.0.jar"); + + createProjectPom("<groupId>test</groupId>" + + "<artifactId>project</artifactId>" + + "<version>1</version>" + + + "<properties>" + + " <depDir>" + new File(libPath).getParent() + "</depDir>" + + "</properties>" + + + "<dependencies>" + + " <dependency>" + + " <groupId>xxx</groupId>" + + " <artifactId>xxx</artifactId>" + + " <version>xxx</version>" + + " <scope>system</scope>" + + " <systemPath>${depDir}/<caret></systemPath>" + + " </dependency>" + + "</dependencies>"); + + assertCompletionVariants(myProjectPom, "junit-4.0.jar"); + } + public void testResolvingSystemScopeDependenciesFromSystemPath() throws Throwable { String libPath = myIndicesFixture.getRepositoryHelper().getTestDataPath("local1/junit/junit/4.0/junit-4.0.jar"); diff --git a/plugins/svn4idea/bindSvn/bindSvn.iml b/plugins/svn4idea/bindSvn/bindSvn.iml deleted file mode 100644 index 846587cef36f..000000000000 --- a/plugins/svn4idea/bindSvn/bindSvn.iml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> - <exclude-output /> - <content url="file://$MODULE_DIR$"> - <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> - </content> - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module" module-name="platform-api" /> - <orderEntry type="module" module-name="vcs-api" /> - <orderEntry type="module-library"> - <library> - <CLASSES> - <root url="jar://$MODULE_DIR$/lib/javahl.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$MODULE_DIR$/lib/javahlsrc.zip!/src" /> - </SOURCES> - </library> - </orderEntry> - </component> -</module> - diff --git a/plugins/svn4idea/bindSvn/lib/javahl.jar b/plugins/svn4idea/bindSvn/lib/javahl.jar Binary files differdeleted file mode 100644 index a0c5370065af..000000000000 --- a/plugins/svn4idea/bindSvn/lib/javahl.jar +++ /dev/null diff --git a/plugins/svn4idea/bindSvn/lib/javahlsrc.zip b/plugins/svn4idea/bindSvn/lib/javahlsrc.zip Binary files differdeleted file mode 100644 index 9c50118203b7..000000000000 --- a/plugins/svn4idea/bindSvn/lib/javahlsrc.zip +++ /dev/null diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindClient.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindClient.java deleted file mode 100644 index c155b78ddc53..000000000000 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindClient.java +++ /dev/null @@ -1,925 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.idea.svn; - -import org.tigris.subversion.javahl.*; - -import java.io.OutputStream; -import java.util.Map; - -/** - * Created with IntelliJ IDEA. - * User: Irina.Chernushina - * Date: 2/5/13 - * Time: 3:08 PM - */ -public class SvnBindClient implements SVNClientInterface { - private final String myExecutablePath; - private CommitEventHandler myHandler; - private AuthenticationCallback myAuthenticationCallback; - - public SvnBindClient(String path) { - myExecutablePath = path; - } - - @Override - public void dispose() { - } - - @Override - public Version getVersion() { - // todo real version - throw new UnsupportedOperationException(); - } - - @Override - public String getAdminDirectoryName() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isAdminDirectory(String name) { - throw new UnsupportedOperationException(); - } - - @Override - public String getLastPath() { - throw new UnsupportedOperationException(); - } - - @Override - public Status singleStatus(String path, boolean onServer) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public Status[] status(String path, boolean descend, boolean onServer, boolean getAll) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public Status[] status(String path, boolean descend, boolean onServer, boolean getAll, boolean noIgnore) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public Status[] status(String path, boolean descend, boolean onServer, boolean getAll, boolean noIgnore, boolean ignoreExternals) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void status(String path, - int depth, - boolean onServer, - boolean getAll, - boolean noIgnore, - boolean ignoreExternals, - String[] changelists, - StatusCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public DirEntry[] list(String url, Revision revision, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public DirEntry[] list(String url, Revision revision, Revision pegRevision, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void list(String url, - Revision revision, - Revision pegRevision, - int depth, - int direntFields, - boolean fetchLocks, - ListCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void username(String username) { - throw new UnsupportedOperationException(); - } - - @Override - public void password(String password) { - throw new UnsupportedOperationException(); - } - - @Override - public void setPrompt(PromptUserPassword prompt) { - throw new UnsupportedOperationException(); - } - - @Override - public LogMessage[] logMessages(String path, Revision revisionStart, Revision revisionEnd) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public LogMessage[] logMessages(String path, Revision revisionStart, Revision revisionEnd, boolean stopOnCopy) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public LogMessage[] logMessages(String path, Revision revisionStart, Revision revisionEnd, boolean stopOnCopy, boolean discoverPath) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public LogMessage[] logMessages(String path, - Revision revisionStart, - Revision revisionEnd, - boolean stopOnCopy, - boolean discoverPath, - long limit) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void logMessages(String path, - Revision pegRevision, - Revision revisionStart, - Revision revisionEnd, - boolean stopOnCopy, - boolean discoverPath, - boolean includeMergedRevisions, - String[] revProps, - long limit, - LogMessageCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void logMessages(String path, - Revision pegRevision, - RevisionRange[] ranges, - boolean stopOnCopy, - boolean discoverPath, - boolean includeMergedRevisions, - String[] revProps, - long limit, - LogMessageCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long checkout(String moduleName, String destPath, Revision revision, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long checkout(String moduleName, - String destPath, - Revision revision, - Revision pegRevision, - boolean recurse, - boolean ignoreExternals) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long checkout(String moduleName, - String destPath, - Revision revision, - Revision pegRevision, - int depth, - boolean ignoreExternals, - boolean allowUnverObstructions) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void notification(Notify notify) { - throw new UnsupportedOperationException(); - } - - @Override - public void notification2(Notify2 notify) { - throw new UnsupportedOperationException(); - } - - @Override - public void setConflictResolver(ConflictResolverCallback listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void setProgressListener(ProgressListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void commitMessageHandler(CommitMessage messageHandler) { - throw new UnsupportedOperationException(); - } - - @Override - public void remove(String[] path, String message, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void remove(String[] path, String message, boolean force, boolean keepLocal, Map revpropTable) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void revert(String path, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void revert(String path, int depth, String[] changelists) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void add(String path, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void add(String path, boolean recurse, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void add(String path, int depth, boolean force, boolean noIgnores, boolean addParents) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long update(String path, Revision revision, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long[] update(String[] path, Revision revision, boolean recurse, boolean ignoreExternals) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long update(String path, - Revision revision, - int depth, - boolean depthIsSticky, - boolean ignoreExternals, - boolean allowUnverObstructions) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long[] update(String[] path, - Revision revision, - int depth, - boolean depthIsSticky, - boolean ignoreExternals, - boolean allowUnverObstructions) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long commit(String[] path, String message, boolean recurse) throws ClientException { - return commit(path, message, recurse? 3 : 0, false, false, null, null); - } - - @Override - public long commit(String[] path, String message, boolean recurse, boolean noUnlock) throws ClientException { - return commit(path, message, recurse? 3 : 0, noUnlock, false, null, null); - } - - @Override - public long commit(String[] path, - String message, - int depth, - boolean noUnlock, - boolean keepChangelist, - String[] changelists, - Map revpropTable) throws ClientException { - final long commit = new SvnCommitRunner(myExecutablePath, myHandler, myAuthenticationCallback). - commit(path, message, depth, noUnlock, keepChangelist, changelists, revpropTable); - if (commit < 0) { - throw new BindClientException("Wrong committed revision number: " + commit, null, -1); - } - return commit; - } - - @Override - public void copy(CopySource[] sources, - String destPath, - String message, - boolean copyAsChild, - boolean makeParents, - boolean ignoreExternals, - Map revpropTable) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void copy(CopySource[] sources, String destPath, String message, boolean copyAsChild, boolean makeParents, Map revpropTable) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void copy(String srcPath, String destPath, String message, Revision revision) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void move(String[] srcPaths, - String destPath, - String message, - boolean force, - boolean moveAsChild, - boolean makeParents, - Map revpropTable) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void move(String srcPath, String destPath, String message, Revision ignored, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void move(String srcPath, String destPath, String message, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void mkdir(String[] path, String message, boolean makeParents, Map revpropTable) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void mkdir(String[] path, String message) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void cleanup(String path) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void resolve(String path, int depth, int conflictResult) throws SubversionException { - throw new UnsupportedOperationException(); - } - - @Override - public void resolved(String path, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long doExport(String srcPath, String destPath, Revision revision, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long doExport(String srcPath, - String destPath, - Revision revision, - Revision pegRevision, - boolean force, - boolean ignoreExternals, - boolean recurse, - String nativeEOL) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long doExport(String srcPath, - String destPath, - Revision revision, - Revision pegRevision, - boolean force, - boolean ignoreExternals, - int depth, - String nativeEOL) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long doSwitch(String path, String url, Revision revision, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long doSwitch(String path, - String url, - Revision revision, - Revision pegRevision, - int depth, - boolean depthIsSticky, - boolean ignoreExternals, - boolean allowUnverObstructions) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void doImport(String path, String url, String message, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void doImport(String path, - String url, - String message, - int depth, - boolean noIgnore, - boolean ignoreUnknownNodeTypes, - Map revpropTable) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public String[] suggestMergeSources(String path, Revision pegRevision) throws SubversionException { - throw new UnsupportedOperationException(); - } - - @Override - public void merge(String path1, Revision revision1, String path2, Revision revision2, String localPath, boolean force, boolean recurse) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void merge(String path1, - Revision revision1, - String path2, - Revision revision2, - String localPath, - boolean force, - boolean recurse, - boolean ignoreAncestry, - boolean dryRun) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void merge(String path1, - Revision revision1, - String path2, - Revision revision2, - String localPath, - boolean force, - int depth, - boolean ignoreAncestry, - boolean dryRun, - boolean recordOnly) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void merge(String path, - Revision pegRevision, - Revision revision1, - Revision revision2, - String localPath, - boolean force, - boolean recurse, - boolean ignoreAncestry, - boolean dryRun) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void merge(String path, - Revision pegRevision, - RevisionRange[] revisions, - String localPath, - boolean force, - int depth, - boolean ignoreAncestry, - boolean dryRun, - boolean recordOnly) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void mergeReintegrate(String path, Revision pegRevision, String localPath, boolean dryRun) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public Mergeinfo getMergeinfo(String path, Revision pegRevision) throws SubversionException { - throw new UnsupportedOperationException(); - } - - @Override - public void getMergeinfoLog(int kind, - String pathOrUrl, - Revision pegRevision, - String mergeSourceUrl, - Revision srcPegRevision, - boolean discoverChangedPaths, - int depth, - String[] revProps, - LogMessageCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void getMergeinfoLog(int kind, - String pathOrUrl, - Revision pegRevision, - String mergeSourceUrl, - Revision srcPegRevision, - boolean discoverChangedPaths, - String[] revProps, - LogMessageCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diff(String target1, Revision revision1, String target2, Revision revision2, String outFileName, boolean recurse) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diff(String target1, - Revision revision1, - String target2, - Revision revision2, - String outFileName, - boolean recurse, - boolean ignoreAncestry, - boolean noDiffDeleted, - boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diff(String target1, - Revision revision1, - String target2, - Revision revision2, - String relativeToDir, - String outFileName, - int depth, - String[] changelists, - boolean ignoreAncestry, - boolean noDiffDeleted, - boolean force, - boolean copiesAsAdds) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diff(String target1, - Revision revision1, - String target2, - Revision revision2, - String relativeToDir, - String outFileName, - int depth, - String[] changelists, - boolean ignoreAncestry, - boolean noDiffDeleted, - boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diff(String target, - Revision pegRevision, - Revision startRevision, - Revision endRevision, - String outFileName, - boolean recurse, - boolean ignoreAncestry, - boolean noDiffDeleted, - boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diff(String target, - Revision pegRevision, - Revision startRevision, - Revision endRevision, - String relativeToDir, - String outFileName, - int depth, - String[] changelists, - boolean ignoreAncestry, - boolean noDiffDeleted, - boolean force, - boolean copiesAsAdds) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diff(String target, - Revision pegRevision, - Revision startRevision, - Revision endRevision, - String relativeToDir, - String outFileName, - int depth, - String[] changelists, - boolean ignoreAncestry, - boolean noDiffDeleted, - boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diffSummarize(String target1, - Revision revision1, - String target2, - Revision revision2, - int depth, - String[] changelists, - boolean ignoreAncestry, - DiffSummaryReceiver receiver) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diffSummarize(String target, - Revision pegRevision, - Revision startRevision, - Revision endRevision, - int depth, - String[] changelists, - boolean ignoreAncestry, - DiffSummaryReceiver receiver) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData[] properties(String path) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData[] properties(String path, Revision revision) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData[] properties(String path, Revision revision, Revision pegRevision) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void properties(String path, Revision revision, Revision pegRevision, int depth, String[] changelists, ProplistCallback callback) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertySet(String path, String name, String value, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertySet(String path, String name, String value, boolean recurse, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertySet(String path, String name, byte[] value, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertySet(String path, String name, byte[] value, boolean recurse, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertySet(String path, String name, String value, int depth, String[] changelists, boolean force, Map revpropTable) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertyRemove(String path, String name, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertyRemove(String path, String name, int depth, String[] changelists) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertyCreate(String path, String name, String value, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertyCreate(String path, String name, String value, boolean recurse, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertyCreate(String path, String name, byte[] value, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertyCreate(String path, String name, byte[] value, boolean recurse, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertyCreate(String path, String name, String value, int depth, String[] changelists, boolean force) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData revProperty(String path, String name, Revision rev) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData[] revProperties(String path, Revision rev) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void setRevProperty(String path, String name, Revision rev, String value, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void setRevProperty(String path, String name, Revision rev, String value, String originalValue, boolean force) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData propertyGet(String path, String name) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData propertyGet(String path, String name, Revision revision) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData propertyGet(String path, String name, Revision revision, Revision pegRevision) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] fileContent(String path, Revision revision) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] fileContent(String path, Revision revision, Revision pegRevision) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void streamFileContent(String path, Revision revision, Revision pegRevision, int bufferSize, OutputStream stream) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void relocate(String from, String to, String path, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] blame(String path, Revision revisionStart, Revision revisionEnd) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void blame(String path, Revision revisionStart, Revision revisionEnd, BlameCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void blame(String path, Revision pegRevision, Revision revisionStart, Revision revisionEnd, BlameCallback callback) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void blame(String path, - Revision pegRevision, - Revision revisionStart, - Revision revisionEnd, - boolean ignoreMimeType, - boolean includeMergedRevisions, - BlameCallback2 callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void blame(String path, - Revision pegRevision, - Revision revisionStart, - Revision revisionEnd, - boolean ignoreMimeType, - boolean includeMergedRevisions, - BlameCallback3 callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void setConfigDirectory(String configDir) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public String getConfigDirectory() throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void cancelOperation() throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public Info info(String path) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void addToChangelist(String[] paths, String changelist, int depth, String[] changelists) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void removeFromChangelists(String[] paths, int depth, String[] changelists) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void getChangelists(String rootPath, String[] changelists, int depth, ChangelistCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void lock(String[] path, String comment, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void unlock(String[] path, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public Info2[] info2(String pathOrUrl, Revision revision, Revision pegRevision, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void info2(String pathOrUrl, Revision revision, Revision pegRevision, int depth, String[] changelists, InfoCallback callback) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public String getVersionInfo(String path, String trailUrl, boolean lastChanged) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void upgrade(String path) throws ClientException { - throw new UnsupportedOperationException(); - } - - public void setHandler(CommitEventHandler handler) { - myHandler = handler; - } - - public void setAuthenticationCallback(AuthenticationCallback authenticationCallback) { - myAuthenticationCallback = authenticationCallback; - } -} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/status/LockWrapper.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/status/LockWrapper.java deleted file mode 100644 index f4e0f4cf2269..000000000000 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/status/LockWrapper.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.idea.svn.status; - -import org.apache.subversion.javahl.types.Lock; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.Date; - -/** - * Created with IntelliJ IDEA. - * User: Irina.Chernushina - * Date: 2/21/12 - * Time: 2:33 PM - */ -public class LockWrapper { - private String myPath; - private String myID; - private String myOwner; - private String myComment; - private Date myCreationDate; - private Date myExpirationDate; - - public LockWrapper(String path, String ID, String owner, String comment, Date creationDate, Date expirationDate) { - myPath = path; - myID = ID; - myOwner = owner; - myComment = comment; - myCreationDate = creationDate; - myExpirationDate = expirationDate; - } - - public LockWrapper() { - } - - public String getPath() { - return myPath; - } - - public void setPath(String path) { - myPath = path; - } - - public String getID() { - return myID; - } - - public void setID(String ID) { - myID = ID; - } - - public String getOwner() { - return myOwner; - } - - public void setOwner(String owner) { - myOwner = owner; - } - - public String getComment() { - return myComment; - } - - public void setComment(String comment) { - myComment = comment; - } - - public Date getCreationDate() { - return myCreationDate; - } - - public void setCreationDate(Date creationDate) { - myCreationDate = creationDate; - } - - public Date getExpirationDate() { - return myExpirationDate; - } - - public void setExpirationDate(Date expirationDate) { - myExpirationDate = expirationDate; - } - - public org.tigris.subversion.javahl.Lock create() { - final Date creation = getCreationDate(); - final Date expiration = getExpirationDate(); - final Lock newLock = new Lock(getOwner(), getPath(), getID(), getComment(), creation == null ? 0 : creation.getTime(), - expiration == null ? 0 : expiration.getTime()); - try { - final Constructor<org.tigris.subversion.javahl.Lock> constructor = org.tigris.subversion.javahl.Lock.class.getConstructor(Lock.class); - constructor.setAccessible(true); - return constructor.newInstance(newLock); - } - catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - catch (InstantiationException e) { - throw new RuntimeException(e); - } - catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } -} diff --git a/plugins/svn4idea/bindSvn/src/org/tigris/subversion/javahl/BindClientException.java b/plugins/svn4idea/bindSvn/src/org/tigris/subversion/javahl/BindClientException.java deleted file mode 100644 index f8acc17d0558..000000000000 --- a/plugins/svn4idea/bindSvn/src/org/tigris/subversion/javahl/BindClientException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.tigris.subversion.javahl; - -import org.jetbrains.annotations.NotNull; - -/** - * Created with IntelliJ IDEA. - * User: Irina.Chernushina - * Date: 2/25/13 - * Time: 6:29 PM - */ -public class BindClientException extends ClientException { - private Throwable myCause; - - public BindClientException(String message, String source, int aprError) { - super(message, source, aprError); - } - - public BindClientException(org.apache.subversion.javahl.ClientException ex) { - super(ex); - } - - public static BindClientException create(@NotNull final Throwable t, final int code) { - final BindClientException exception = new BindClientException(t.getMessage(), null, code); - exception.myCause = t; - return exception; - } - - @Override - public Throwable getCause() { - return myCause; - } -} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/ForNestedRootChecker.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/ForNestedRootChecker.java index 4559a3e603e2..6a62276b3a1a 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/ForNestedRootChecker.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/ForNestedRootChecker.java @@ -49,11 +49,9 @@ public class ForNestedRootChecker { private static class UrlConstructor { final SvnVcs myVcs; - final SVNWCClient myClient; private UrlConstructor(final SvnVcs vcs) { myVcs = vcs; - myClient = myVcs.createWCClient(); } @Nullable diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/RootsToWorkingCopies.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/RootsToWorkingCopies.java index 6cd0df2744d1..5ca7f9eaa39b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/RootsToWorkingCopies.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/RootsToWorkingCopies.java @@ -123,52 +123,31 @@ public class RootsToWorkingCopies implements VcsListener { @Nullable private WorkingCopy calculateRoot(final VirtualFile root) { + File workingCopyRoot = SvnUtil.getWorkingCopyRootNew(new File(root.getPath())); WorkingCopy workingCopy = null; - final File ioFile = new File(root.getPath()); - File workingCopyRoot = null; - try { - workingCopyRoot = SVNWCUtil.getWorkingCopyRoot(ioFile, true); - if (workingCopyRoot != null) { - // ok to use low-level 1.6 API, 1.7 is checked below - SVNWCAccess wcAccess = SVNWCAccess.newInstance(null); - try { - wcAccess.probeOpen(workingCopyRoot, false, 0); - SVNEntry entry = wcAccess.getVersionedEntry(workingCopyRoot, false); - final SVNURL url = entry.getSVNURL(); - if (url != null) { - workingCopy = new WorkingCopy(workingCopyRoot, url, false); - } - } finally { - wcAccess.close(); - } - } - } - catch (SVNException e) { - // - } - if (workingCopy == null) { - workingCopyRoot = SvnUtil.getWcCopyRootIf17(ioFile, null); - if (workingCopyRoot != null) { - final SVNInfo svnInfo; - try { - svnInfo = SvnVcs.getInstance(myProject).createWCClient().doInfo(workingCopyRoot, SVNRevision.UNDEFINED); - workingCopy = new WorkingCopy(workingCopyRoot, svnInfo.getURL(), true); - } - catch (SVNException e) { - // - } + + if (workingCopyRoot != null) { + final SVNInfo svnInfo = myVcs.getInfo(workingCopyRoot); + + if (svnInfo != null && svnInfo.getURL() != null) { + workingCopy = new WorkingCopy(workingCopyRoot, svnInfo.getURL(), true); } } + + return registerWorkingCopy(root, workingCopy); + } + + private WorkingCopy registerWorkingCopy(@NotNull VirtualFile root, @Nullable WorkingCopy resolvedWorkingCopy) { synchronized (myLock) { - if (workingCopy == null) { + if (resolvedWorkingCopy == null) { myRootMapping.remove(root); myUnversioned.add(root); } else { myUnversioned.remove(root); - myRootMapping.put(root, workingCopy); + myRootMapping.put(root, resolvedWorkingCopy); } } - return workingCopy; + return resolvedWorkingCopy; } public void clear() { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAbstractWriteOperationLocks.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAbstractWriteOperationLocks.java index b4fd10492dfb..29e87422bb53 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAbstractWriteOperationLocks.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAbstractWriteOperationLocks.java @@ -36,6 +36,7 @@ import java.util.concurrent.locks.ReentrantLock; * Date: 10/19/12 * Time: 12:09 PM */ +// TODO: Such locking functionality is not required anymore. Likely to be removed (together with SvnProxies). public abstract class SvnAbstractWriteOperationLocks { private final long myTimeout; private final static Map<String, Lock> myLockMap = new HashMap<String, Lock>(); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java index 8a94f0076acf..4efcce6f0db6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java @@ -350,7 +350,9 @@ public class SvnAuthenticationNotifier extends GenericNotifierImpl<SvnAuthentica } SvnInteractiveAuthenticationProvider.clearCallState(); try { + // start svnkit authentication cycle SvnVcs.getInstance(project).createWCClient(manager).doInfo(url, SVNRevision.UNDEFINED, SVNRevision.HEAD); + //SvnVcs.getInstance(project).getInfo(url, SVNRevision.HEAD, manager); } catch (SVNAuthenticationException e) { log(e); return false; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties index c306386eb0f8..61a5bb26f663 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties @@ -537,6 +537,7 @@ dialog.show.svn.map.table.version14.text=1.4 dialog.show.svn.map.table.version15.text=1.5 dialog.show.svn.map.table.version16.text=1.6 dialog.show.svn.map.table.version17.text=1.7 +dialog.show.svn.map.table.version18.text=1.8 action.change.wcopy.format.task.title=Convert working copy format action.change.wcopy.format.task.progress.text=Converting working copy at {0} format from {1} to {2} action.change.wcopy.format.after.change.settings=Selected working copy format ({0}) is older than upgrade mode selected in Project Settings ({1}).\ diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java index dc8cbb1cce8b..b28ebd53dde2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java @@ -85,7 +85,7 @@ public class SvnChangeProvider implements ChangeProvider { statusReceiver.addListener(context); statusReceiver.addListener(nestedCopiesBuilder); - final SvnRecursiveStatusWalker walker = new SvnRecursiveStatusWalker(myVcs.getProject(), statusReceiver.getMulticaster(), partner); + final SvnRecursiveStatusWalker walker = new SvnRecursiveStatusWalker(myVcs, statusReceiver.getMulticaster(), partner); for (FilePath path : zipper.getRecursiveDirs()) { walker.go(path, SVNDepth.INFINITY); @@ -180,7 +180,7 @@ public class SvnChangeProvider implements ChangeProvider { public void getChanges(final FilePath path, final boolean recursive, final ChangelistBuilder builder) throws SVNException { final SvnChangeProviderContext context = new SvnChangeProviderContext(myVcs, builder, null); final StatusWalkerPartnerImpl partner = new StatusWalkerPartnerImpl(myVcs, ProgressManager.getInstance().getProgressIndicator()); - final SvnRecursiveStatusWalker walker = new SvnRecursiveStatusWalker(myVcs.getProject(), context, partner); + final SvnRecursiveStatusWalker walker = new SvnRecursiveStatusWalker(myVcs, context, partner); walker.go(path, recursive ? SVNDepth.INFINITY : SVNDepth.IMMEDIATES); processCopiedAndDeleted(context, null); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java index 40c34d394590..9689dcee4a71 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java @@ -259,23 +259,15 @@ class SvnChangeProviderContext implements StatusReceiver { public void addModifiedNotSavedChange(final VirtualFile file) throws SVNException { final FilePath filePath = new FilePathImpl(file); - final SVNInfo svnInfo; - try { - svnInfo = myVcs.createWCClient().doInfo(new File(file.getPath()), SVNRevision.UNDEFINED); + final SVNInfo svnInfo = myVcs.getInfo(file); + + if (svnInfo != null) { + final SVNStatus svnStatus = new SVNStatus(); + svnStatus.setRevision(svnInfo.getRevision()); + myChangelistBuilder.processChangeInList( + createChange(SvnContentRevision.createBaseRevision(myVcs, filePath, svnInfo.getRevision()), CurrentContentRevision.create(filePath), + FileStatus.MODIFIED, svnStatus), (String)null, SvnVcs.getKey()); } - catch (SVNException e) { - final SVNErrorCode errorCode = e.getErrorMessage().getErrorCode(); - if (SVNErrorCode.WC_PATH_NOT_FOUND.equals(errorCode) || - SVNErrorCode.UNVERSIONED_RESOURCE.equals(errorCode) || SVNErrorCode.WC_NOT_WORKING_COPY.equals(errorCode)) { - return; - } - throw e; - } - final SVNStatus svnStatus = new SVNStatus(); - svnStatus.setRevision(svnInfo.getRevision()); - myChangelistBuilder.processChangeInList(createChange(SvnContentRevision.createBaseRevision(myVcs, filePath, svnInfo.getRevision()), - CurrentContentRevision.create(filePath), FileStatus.MODIFIED, svnStatus), (String) null, - SvnVcs.getKey()); } private void checkSwitched(final FilePath filePath, final ChangelistBuilder builder, final SVNStatus status, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangelistListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangelistListener.java index ee326a42afb2..51d5dc486c51 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangelistListener.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangelistListener.java @@ -176,11 +176,9 @@ public class SvnChangelistListener implements ChangeListListener { } @Nullable - public static String getCurrentMapping(final Project project, final File file) { - final SvnVcs vcs = SvnVcs.getInstance(project); - final SVNStatusClient statusClient = vcs.createStatusClient(); + public static String getCurrentMapping(final SvnVcs vcs, final File file) { try { - final SVNStatus status = statusClient.doStatus(file, false); + final SVNStatus status = vcs.getFactory(file).createStatusClient().doStatus(file, false); return status == null ? null : status.getChangelistName(); } catch (SVNException e) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.form b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.form index e4f0e7c8ae7e..55f7512fbb81 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.form +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.form @@ -18,7 +18,7 @@ <properties/> <border type="none"/> <children> - <grid id="2ae3a" layout-manager="GridLayoutManager" row-count="6" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <grid id="2ae3a" layout-manager="GridLayoutManager" row-count="5" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> <tabbedpane title="General"/> @@ -29,7 +29,7 @@ <grid id="a4729" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> - <grid row="3" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> + <grid row="2" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> </constraints> <properties/> <border type="none"/> @@ -87,7 +87,7 @@ </grid> <component id="df054" class="javax.swing.JCheckBox" binding="myUseDefaultCheckBox"> <constraints> - <grid row="2" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + <grid row="1" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/> </constraints> <properties> <text resource-bundle="org/jetbrains/idea/svn/SvnBundle" key="checkbox.configure.use.system.default.configuration.directory"/> @@ -96,7 +96,7 @@ <grid id="137aa" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> - <grid row="5" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> + <grid row="4" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> </constraints> <properties/> <border type="none"/> @@ -123,36 +123,29 @@ </grid> <hspacer id="e8f24"> <constraints> - <grid row="5" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/> + <grid row="4" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/> </constraints> </hspacer> <vspacer id="aa6b2"> <constraints> - <grid row="4" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> + <grid row="3" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> </constraints> </vspacer> - <component id="300b5" class="javax.swing.JLabel"> - <constraints> - <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> - </constraints> - <properties> - <text value="Subversion 1.7 Acceleration"/> - </properties> - </component> <grid id="12735" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> - <grid row="1" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> + <grid row="0" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> </constraints> <properties/> <border type="none"/> <children> <component id="3ecd1" class="com.intellij.ui.components.JBCheckBox" binding="myWithCommandLineClient"> <constraints> - <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="2" use-parent-layout="false"/> + <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> </constraints> <properties> - <text value="with command line client:"/> + <margin top="2" left="3" bottom="2" right="3"/> + <text value="Use command line client:"/> </properties> </component> <component id="44a8" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myCommandLineClient"> diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnContentRevision.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnContentRevision.java index 38c099ba4136..cff64beea8b2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnContentRevision.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnContentRevision.java @@ -31,6 +31,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNStatus; +import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; import java.io.IOException; @@ -113,7 +114,8 @@ public class SvnContentRevision implements ContentRevision, MarkerVcsContentRevi if (lock.exists()) { throw new VcsException("Can not access file base revision contents: administrative area is locked"); } - return SvnUtil.getFileContents(myVcs, file.getPath(), false, myUseBaseRevision ? SVNRevision.BASE : myRevision, SVNRevision.UNDEFINED); + return SvnUtil.getFileContents(myVcs, SvnTarget.fromFile(file), myUseBaseRevision ? SVNRevision.BASE : myRevision, + SVNRevision.UNDEFINED); } @NotNull diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java index 2a8a00aa1ae6..813d41c48e24 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java @@ -17,6 +17,7 @@ package org.jetbrains.idea.svn; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.actions.VcsContextFactory; import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.diff.DiffMixin; @@ -26,8 +27,10 @@ import com.intellij.openapi.vcs.history.VcsRevisionDescription; import com.intellij.openapi.vcs.history.VcsRevisionDescriptionImpl; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient; import org.jetbrains.idea.svn.history.LatestExistentSearcher; import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.*; @@ -43,19 +46,12 @@ public class SvnDiffProvider implements DiffProvider, DiffMixin { } public VcsRevisionNumber getCurrentRevision(VirtualFile file) { - final SVNWCClient client = myVcs.createWCClient(); - try { - final SVNInfo svnInfo = client.doInfo(new File(file.getPresentableUrl()), SVNRevision.UNDEFINED); - if (svnInfo == null) return null; - if (SVNRevision.UNDEFINED.equals(svnInfo.getCommittedRevision()) && svnInfo.getCopyFromRevision() != null) { - return new SvnRevisionNumber(svnInfo.getCopyFromRevision()); - } - return new SvnRevisionNumber(svnInfo.getRevision()); - } - catch (SVNException e) { - LOG.debug(e); // most likely the file is unversioned - return null; + final SVNInfo svnInfo = myVcs.getInfo(new File(file.getPresentableUrl())); + if (svnInfo == null) return null; + if (SVNRevision.UNDEFINED.equals(svnInfo.getCommittedRevision()) && svnInfo.getCopyFromRevision() != null) { + return new SvnRevisionNumber(svnInfo.getCopyFromRevision()); } + return new SvnRevisionNumber(svnInfo.getRevision()); } @Override @@ -65,53 +61,36 @@ public class SvnDiffProvider implements DiffProvider, DiffMixin { } private VcsRevisionDescription getCurrentRevisionDescription(File path) { - final SVNWCClient client = myVcs.createWCClient(); - try { - final SVNInfo svnInfo = client.doInfo(path, SVNRevision.UNDEFINED); - - if (svnInfo.getCommittedRevision().equals(SVNRevision.UNDEFINED) && ! svnInfo.getCopyFromRevision().equals(SVNRevision.UNDEFINED) && + final SVNInfo svnInfo = myVcs.getInfo(path); + if (svnInfo == null) { + return null; + } + + if (svnInfo.getCommittedRevision().equals(SVNRevision.UNDEFINED) && !svnInfo.getCopyFromRevision().equals(SVNRevision.UNDEFINED) && svnInfo.getCopyFromURL() != null) { - SVNURL copyUrl = svnInfo.getCopyFromURL(); - String localPath = myVcs.getSvnFileUrlMapping().getLocalPath(copyUrl.toString()); - if (localPath != null) { - return getCurrentRevisionDescription(new File(localPath)); - } + SVNURL copyUrl = svnInfo.getCopyFromURL(); + String localPath = myVcs.getSvnFileUrlMapping().getLocalPath(copyUrl.toString()); + if (localPath != null) { + return getCurrentRevisionDescription(new File(localPath)); } - final String message = getProperties(client, path); + } + + try { + final String message = getCommitMessage(path); return new VcsRevisionDescriptionImpl(new SvnRevisionNumber(svnInfo.getCommittedRevision()), svnInfo.getCommittedDate(), svnInfo.getAuthor(), message); } - catch (SVNException e) { + catch (VcsException e) { LOG.debug(e); // most likely the file is unversioned return null; } } - private String getProperties(SVNWCClient client, File path) throws SVNException { - final String[] message = new String[1]; - client.doGetRevisionProperty(path, null, SVNRevision.BASE, new ISVNPropertyHandler() { - @Override - public void handleProperty(File path, SVNPropertyData property) throws SVNException { - handle(property); - } - - @Override - public void handleProperty(SVNURL url, SVNPropertyData property) throws SVNException { - handle(property); - } - - @Override - public void handleProperty(long revision, SVNPropertyData property) throws SVNException { - handle(property); - } + private String getCommitMessage(File path) throws VcsException { + SVNPropertyData property = + myVcs.getFactory(path).createPropertyClient().getProperty(path, COMMIT_MESSAGE, true, null, SVNRevision.BASE); - private void handle(SVNPropertyData data) { - if (COMMIT_MESSAGE.equals(data.getName())) { - message[0] = data.getValue().getString(); - } - } - }); - return message[0]; + return property != null ? SVNPropertyValue.getPropertyAsString(property.getValue()) : null; } private static ItemLatestState defaultResult() { @@ -135,18 +114,45 @@ public class SvnDiffProvider implements DiffProvider, DiffMixin { return SvnContentRevision.createBaseRevision(myVcs, filePath, svnRevision); } } + // not clear why we need it, with remote check.. - final SVNStatusClient client = myVcs.createStatusClient(); - try { - final SVNStatus svnStatus = client.doStatus(new File(selectedFile.getPresentableUrl()), false, false); - if (svnRevision.equals(svnStatus.getRevision())) { + SVNStatus svnStatus = getFileStatus(new File(selectedFile.getPresentableUrl()), false); + if (svnStatus != null && svnRevision.equals(svnStatus.getRevision())) { return SvnContentRevision.createBaseRevision(myVcs, filePath, svnRevision); - } + } + return SvnContentRevision.createRemote(myVcs, filePath, svnRevision); + } + + private SVNStatus getFileStatus(File file, boolean remote) { + WorkingCopyFormat format = myVcs.getWorkingCopyFormat(file); + + return WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) ? getStatusCommandLine(file, remote) : getStatusWithSvnKit(file, remote); + } + + private SVNStatus getStatusWithSvnKit(File file, boolean remote) { + SVNStatus result = null; + + try { + result = myVcs.createStatusClient().doStatus(file, remote, false); } catch (SVNException e) { LOG.debug(e); // most likely the file is unversioned } - return SvnContentRevision.createRemote(myVcs, filePath, svnRevision); + + return result; + } + + private SVNStatus getStatusCommandLine(File file, boolean remote) { + SVNStatus result = null; + + try { + result = new SvnCommandLineStatusClient(myVcs.getProject()).doStatus(file, remote, false); + } + catch (SVNException e) { + LOG.debug(e); + } + + return result; } public ItemLatestState getLastRevision(FilePath filePath) { @@ -159,36 +165,29 @@ public class SvnDiffProvider implements DiffProvider, DiffMixin { } private ItemLatestState getLastRevision(final File file) { - final SVNStatusClient client = myVcs.createStatusClient(); - try { - final SVNStatus svnStatus = client.doStatus(file, true); - if (svnStatus == null || itemExists(svnStatus) && SVNRevision.UNDEFINED.equals(svnStatus.getRemoteRevision())) { - // IDEADEV-21785 (no idea why this can happen) - final SVNInfo info = myVcs.createWCClient().doInfo(file, SVNRevision.HEAD); - if (info == null || info.getURL() == null) { - LOG.info("No SVN status returned for " + file.getPath()); - return defaultResult(); - } - return createResult(info.getCommittedRevision(), true, false); + final SVNStatus svnStatus = getFileStatus(file, true); + if (svnStatus == null || itemExists(svnStatus) && SVNRevision.UNDEFINED.equals(svnStatus.getRemoteRevision())) { + // IDEADEV-21785 (no idea why this can happen) + final SVNInfo info = myVcs.getInfo(file, SVNRevision.HEAD); + if (info == null || info.getURL() == null) { + LOG.info("No SVN status returned for " + file.getPath()); + return defaultResult(); } - final boolean exists = itemExists(svnStatus); - if (! exists) { - // get really latest revision - final LatestExistentSearcher searcher = new LatestExistentSearcher(myVcs, svnStatus.getURL()); - final long revision = searcher.getDeletionRevision(); + return createResult(info.getCommittedRevision(), true, false); + } + final boolean exists = itemExists(svnStatus); + if (! exists) { + // get really latest revision + final LatestExistentSearcher searcher = new LatestExistentSearcher(myVcs, svnStatus.getURL()); + final long revision = searcher.getDeletionRevision(); - return createResult(SVNRevision.create(revision), exists, false); - } - final SVNRevision remoteRevision = svnStatus.getRemoteRevision(); - if (remoteRevision != null) { - return createResult(remoteRevision, exists, false); - } - return createResult(svnStatus.getRevision(), exists, false); + return createResult(SVNRevision.create(revision), exists, false); } - catch (SVNException e) { - LOG.debug(e); // most likely the file is unversioned - return defaultResult(); + final SVNRevision remoteRevision = svnStatus.getRemoteRevision(); + if (remoteRevision != null) { + return createResult(remoteRevision, exists, false); } + return createResult(svnStatus.getRevision(), exists, false); } private boolean itemExists(SVNStatus svnStatus) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java index 062c412f2336..da1adc60327c 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java @@ -44,10 +44,7 @@ import com.intellij.util.containers.MultiMap; import com.intellij.vcsUtil.ActionWithTempFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.tmatesoft.svn.core.SVNErrorCode; -import org.tmatesoft.svn.core.SVNErrorMessage; -import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNNodeKind; +import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.wc.SVNFileUtil; import org.tmatesoft.svn.core.wc.*; @@ -160,10 +157,10 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp } private class UUIDHelper { - private final SVNWCClient myWcClient; + private final SvnVcs myVcs; private UUIDHelper(final SvnVcs vcs) { - myWcClient = vcs.createWCClient(); + myVcs = vcs; } /** @@ -175,7 +172,7 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp final SVNInfo info1 = new RepeatSvnActionThroughBusy() { @Override protected void executeImpl() throws SVNException { - myT = myWcClient.doInfo(new File(dir.getPath()), SVNRevision.UNDEFINED); + myT = myVcs.getInfo(new File(dir.getPath())); } }.compute(); if (info1 == null || info1.getRepositoryUUID() == null) { @@ -244,10 +241,11 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp long srcTime = src.lastModified(); try { final boolean isUndo = isUndo(vcs); - final String list = isUndo ? null : SvnChangelistListener.getCurrentMapping(vcs.getProject(), src); + final String list = isUndo ? null : SvnChangelistListener.getCurrentMapping(vcs, src); - final boolean is17 = SvnUtil.is17CopyPart(src); - if (is17) { + WorkingCopyFormat format = vcs.getWorkingCopyFormat(src); + final boolean is17OrLater = WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) || WorkingCopyFormat.ONE_DOT_SEVEN.equals(format); + if (is17OrLater) { SVNStatus srcStatus = getFileStatus(vcs, src); final File toDir = dst.getParentFile(); SVNStatus dstStatus = getFileStatus(vcs, toDir); @@ -283,29 +281,18 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp ourStatusesForUndoMove.add(SVNStatusType.STATUS_ADDED); } - private boolean for17move(SvnVcs vcs, final File src, final File dst, boolean undo, SVNStatus srcStatus) throws SVNException { + private boolean for17move(final SvnVcs vcs, final File src, final File dst, boolean undo, SVNStatus srcStatus) throws SVNException { if (srcStatus != null && srcStatus.getCopyFromURL() == null) { undo = false; } if (undo) { myUndoingMove = true; - final SVNWCClient wcClient = vcs.createWCClient(); - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doRevert(dst, true); - } - }.execute(); + createRevertAction(vcs, dst, true).execute(); copyUnversionedMembersOfDirectory(src, dst); if (srcStatus == null || SvnVcs.svnStatusIsUnversioned(srcStatus)) { FileUtil.delete(src); } else { - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doRevert(src, true); - } - }.execute(); + createRevertAction(vcs, src, true).execute(); } restoreFromUndoStorage(dst); } else { @@ -317,15 +304,9 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp copyFileOrDir(src, dst); } catch (IOException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } - final SVNWCClient wcClient = vcs.createWCClient(); - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doDelete(src, true, false); - } - }.execute(); + createDeleteAction(vcs, src, true).execute(); return false; } moveFileWithSvn(vcs, src, dst); @@ -333,13 +314,16 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp return false; } - public static void moveFileWithSvn(SvnVcs vcs, File src, final File dst) throws SVNException { - final SVNCopyClient copyClient = vcs.createCopyClient(); - final SVNCopySource svnCopySource = new SVNCopySource(SVNRevision.UNDEFINED, SVNRevision.WORKING, src); + public static void moveFileWithSvn(final SvnVcs vcs, final File src, final File dst) throws SVNException { new RepeatSvnActionThroughBusy() { @Override protected void executeImpl() throws SVNException { - copyClient.doCopy(new SVNCopySource[]{svnCopySource}, dst, true, false, true); + try { + vcs.getFactory(src).createCopyMoveClient().copy(src, dst, false, true); + } + catch (VcsException e) { + wrapAndThrow(e); + } } }.execute(); } @@ -357,7 +341,7 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp copyFileOrDir(src, dst); } catch (IOException e) { - exc[0] = new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + exc[0] = new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); return false; } } @@ -464,7 +448,7 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp * deleted: do nothing, return true (strange) */ public boolean delete(VirtualFile file) throws IOException { - SvnVcs vcs = getVCS(file); + final SvnVcs vcs = getVCS(file); if (vcs != null && SvnUtil.isAdminDirectory(file)) { return true; } @@ -476,12 +460,8 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp if (! SvnUtil.isSvnVersioned(vcs.getProject(), ioFile.getParentFile())) { return false; } - try { - if (SVNWCUtil.isWorkingCopyRoot(ioFile)) { - return false; - } - } catch (SVNException e) { - // + if (SvnUtil.isWorkingCopyRoot(ioFile)) { + return false; } SVNStatus status = getFileStatus(vcs, ioFile); @@ -507,13 +487,7 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp } if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_ADDED)) { try { - final SVNWCClient wcClient = vcs.createWCClient(); - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doRevert(ioFile, false); - } - }.execute(); + createRevertAction(vcs, ioFile, false).execute(); } catch (SVNException e) { // ignore @@ -529,6 +503,41 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp } } + @NotNull + private RepeatSvnActionThroughBusy createRevertAction(@NotNull final SvnVcs vcs, @NotNull final File file, final boolean recursive) { + return new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + try { + vcs.getFactory(file).createRevertClient().revert(new File[]{file}, SVNDepth.fromRecurse(recursive), null); + } + catch (VcsException e) { + wrapAndThrow(e); + } + } + }; + } + + @NotNull + private RepeatSvnActionThroughBusy createDeleteAction(@NotNull final SvnVcs vcs, @NotNull final File file, final boolean force) { + return new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + try { + vcs.getFactory(file).createDeleteClient().delete(file, force); + } + catch (VcsException e) { + wrapAndThrow(e); + } + } + }; + } + + private static void wrapAndThrow(VcsException e) throws SVNException { + // TODO: probably we should wrap into new exception only if e.getCause is not SVNException + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.FS_GENERAL, e), e); + } + private boolean isAboveSourceOfCopyOrMove(final Project p, File ioFile) { for (MovedFileInfo file : myMovedFiles) { if (FileUtil.isAncestor(ioFile, file.mySrc, false)) return true; @@ -583,7 +592,6 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp if (! SvnUtil.isSvnVersioned(vcs.getProject(), ioDir) && ! pendingAdd) { return false; } - final SVNWCClient wcClient = vcs.createWCClient(); final File targetFile = new File(ioDir, name); SVNStatus status = getFileStatus(vcs, targetFile); @@ -603,12 +611,7 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp } try { if (isUndo(vcs)) { - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doRevert(targetFile, false); - } - }.execute(); + createRevertAction(vcs, targetFile, false).execute(); return true; } myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(dir, name, null, recursive)); @@ -759,8 +762,6 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp return new Runnable() { @Override public void run() { - final SVNWCClient wcClient = vcs.createWCClient(); - final SVNCopyClient copyClient = vcs.createCopyClient(); for(VirtualFile file: filesToProcess) { final File ioFile = new File(file.getPath()); try { @@ -771,11 +772,15 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp protected void executeInternal() throws VcsException { try { // not recursive - final SVNCopySource[] copySource = {new SVNCopySource(SVNRevision.WORKING, SVNRevision.WORKING, copyFrom)}; new RepeatSvnActionThroughBusy() { @Override protected void executeImpl() throws SVNException { - copyClient.doCopy(copySource, ioFile, false, true, true); + try { + vcs.getFactory(copyFrom).createCopyMoveClient().copy(copyFrom, ioFile, true, false); + } + catch (VcsException e) { + wrapAndThrow(e); + } } }.execute(); } @@ -793,7 +798,12 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp new RepeatSvnActionThroughBusy() { @Override protected void executeImpl() throws SVNException { - wcClient.doAdd(ioFile, true, false, false, true); + try { + vcs.getFactory(ioFile).createAddClient().add(ioFile, null, false, false, true, null); + } + catch (VcsException e) { + wrapAndThrow(e); + } } }.execute(); } @@ -915,17 +925,11 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp final List<VcsException> exceptions) { return new Runnable() { public void run() { - final SVNWCClient wcClient = vcs.createWCClient(); for(FilePath file: filesToProcess) { VirtualFile vFile = file.getVirtualFile(); // for deleted directories final File ioFile = new File(file.getPath()); try { - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doDelete(ioFile, true, false); - } - }.execute(); + createDeleteAction(vcs, ioFile, true).execute(); if (vFile != null && vFile.isValid() && vFile.isDirectory()) { vFile.refresh(true, true); VcsDirtyScopeManager.getInstance(project).dirDirtyRecursively(vFile); @@ -978,18 +982,15 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp private void fillDeletedFiles(Project project, List<Pair<FilePath, WorkingCopyFormat>> deletedFiles, Collection<FilePath> deleteAnyway) throws SVNException { final SvnVcs vcs = SvnVcs.getInstance(project); - final SVNStatusClient sc = vcs.createStatusClient(); final Collection<File> files = myDeletedFiles.remove(project); for (final File file : files) { - boolean isAdded = false; - final SVNStatus status; - status = new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - myT = sc.doStatus(file, false); - } - }.compute(); - isAdded = SVNStatusType.STATUS_ADDED.equals(status.getNodeStatus()); + final SVNStatus status = new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + myT = vcs.getFactory(file).createStatusClient().doStatus(file, false); + } + }.compute(); + boolean isAdded = SVNStatusType.STATUS_ADDED.equals(status.getNodeStatus()); final FilePath filePath = VcsContextFactory.SERVICE.getInstance().createFilePathOn(file); if (isAdded) { deleteAnyway.add(filePath); @@ -1034,18 +1035,12 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp } @Nullable - private static SVNStatus getFileStatus(SvnVcs vcs, File file) { - SVNStatusClient stClient = vcs.createStatusClient(); - return getFileStatus(file, stClient); - } - - @Nullable - private static SVNStatus getFileStatus(final File file, final SVNStatusClient stClient) { + private static SVNStatus getFileStatus(@NotNull final SvnVcs vcs, @NotNull final File file) { try { return new RepeatSvnActionThroughBusy() { @Override protected void executeImpl() throws SVNException { - myT = stClient.doStatus(file, false); + myT = vcs.getFactory(file).createStatusClient().doStatus(file, false); } }.compute(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java index 46545c43a4cc..68e7c07848c2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java @@ -312,8 +312,8 @@ public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentState LOG.info("Error: cannot find repository URL for versioned folder: " + foundRoot.getFile().getPath()); } else { myRepositoryRoots.register(repoRoot); - myTopRoots.add(new RootUrlInfo(repoRoot, foundRoot.getInfo().getURL(), - SvnFormatSelector.getWorkingCopyFormat(new File(foundRoot.getFile().getPath())), foundRoot.getFile(), vcsRoot)); + myTopRoots.add(new RootUrlInfo(repoRoot, foundRoot.getInfo().getURL(), SvnFormatSelector.findRootAndGetFormat( + new File(foundRoot.getFile().getPath())), foundRoot.getFile(), vcsRoot)); } } } @@ -541,8 +541,9 @@ public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentState final SVNInfo svnInfo = vcs.getInfo(copyRoot); if ((svnInfo == null) || (svnInfo.getRepositoryRootURL() == null)) continue; - final RootUrlInfo info = new RootUrlInfo(svnInfo.getRepositoryRootURL(), svnInfo.getURL(), - SvnFormatSelector.getWorkingCopyFormat(svnInfo.getFile()), copyRoot, vcsRoot); + final RootUrlInfo info = + new RootUrlInfo(svnInfo.getRepositoryRootURL(), svnInfo.getURL(), SvnFormatSelector.findRootAndGetFormat(svnInfo.getFile()), + copyRoot, vcsRoot); mapping.add(info); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java index 94d21dbbbd9d..77783badc2f8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java @@ -16,6 +16,7 @@ package org.jetbrains.idea.svn; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; import com.intellij.util.WaitForProgressToShow; @@ -37,6 +38,9 @@ import java.util.Collections; import java.util.Iterator; public class SvnFormatSelector implements ISVNAdminAreaFactorySelector { + + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnFormatSelector"); + public Collection getEnabledFactories(File path, Collection factories, boolean writeAccess) throws SVNException { if (ApplicationManager.getApplication().isUnitTestMode()) { return factories; @@ -145,7 +149,19 @@ public class SvnFormatSelector implements ISVNAdminAreaFactorySelector { return newMode[0]; } + public static WorkingCopyFormat findRootAndGetFormat(final File path) { + File root = SvnUtil.getWorkingCopyRootNew(path); + + return root != null ? getWorkingCopyFormat(root) : WorkingCopyFormat.UNKNOWN; + } + public static WorkingCopyFormat getWorkingCopyFormat(final File path) { + WorkingCopyFormat format = SvnUtil.getFormat(path); + + return WorkingCopyFormat.UNKNOWN.equals(format) ? detectWithSvnKit(path) : format; + } + + private static WorkingCopyFormat detectWithSvnKit(File path) { try { final SvnWcGeneration svnWcGeneration = SvnOperationFactory.detectWcGeneration(path, true); if (SvnWcGeneration.V17.equals(svnWcGeneration)) return WorkingCopyFormat.ONE_DOT_SEVEN; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnProxies.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnProxies.java index 97b87efaa7f9..fcac3966c2d6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnProxies.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnProxies.java @@ -29,6 +29,7 @@ import java.util.concurrent.atomic.AtomicReference; * Date: 10/19/12 * Time: 4:22 PM */ +// TODO: Likely to be removed (together with SvnAbstractWriteOperationLocks). public class SvnProxies { private final SvnAbstractWriteOperationLocks myLocks; private final AtomicReference<LearningProxy<SVNChangelistClientI, SVNException>> myChangelist; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java index 4a49dc16a153..8b518199f5be 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java @@ -47,13 +47,15 @@ import java.util.LinkedList; public class SvnRecursiveStatusWalker { private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnRecursiveStatusWalker"); private final StatusWalkerPartner myPartner; + private final SvnVcs myVcs; private final Project myProject; private final StatusReceiver myReceiver; private final LinkedList<MyItem> myQueue; private final MyHandler myHandler; - public SvnRecursiveStatusWalker(final Project project, final StatusReceiver receiver, final StatusWalkerPartner partner) { - myProject = project; + public SvnRecursiveStatusWalker(final SvnVcs vcs, final StatusReceiver receiver, final StatusWalkerPartner partner) { + myVcs = vcs; + myProject = vcs.getProject(); myReceiver = receiver; myPartner = partner; myQueue = new LinkedList<MyItem>(); @@ -61,7 +63,7 @@ public class SvnRecursiveStatusWalker { } public void go(final FilePath rootPath, final SVNDepth depth) throws SVNException { - final MyItem root = new MyItem(myProject, rootPath, depth, myPartner.createStatusClient(), false); + final MyItem root = new MyItem(myVcs, rootPath, depth, myPartner.createStatusClient(), false); myQueue.add(root); while (! myQueue.isEmpty()) { @@ -83,7 +85,7 @@ public class SvnRecursiveStatusWalker { } } else { try { - final SVNStatus status = item.getClient().doStatus(ioFile, false, false); + final SVNStatus status = item.getClient(ioFile).doStatus(ioFile, false, false); myReceiver.process(path, status); } catch (SVNException e) { handleStatusException(item, path, e); @@ -115,18 +117,20 @@ public class SvnRecursiveStatusWalker { private final Project myProject; private final FilePath myPath; private final SVNDepth myDepth; - private final SVNStatusClient myClient; private final SvnStatusClientI mySvnClient; + private final SvnStatusClientI myCommandLineClient; private final boolean myIsInnerCopyRoot; private final SvnConfiguration myConfiguration17; + private final SvnVcs myVcs; - private MyItem(Project project, FilePath path, SVNDepth depth, SVNStatusClient client, boolean isInnerCopyRoot) { - myProject = project; + private MyItem(SvnVcs vcs, FilePath path, SVNDepth depth, SVNStatusClient client, boolean isInnerCopyRoot) { + myVcs = vcs; + myProject = vcs.getProject(); myConfiguration17 = SvnConfiguration.getInstance(myProject); myPath = path; myDepth = depth; - myClient = client; mySvnClient = new SvnkitSvnStatusClient(client); + myCommandLineClient = new SvnCommandLineStatusClient(myProject); myIsInnerCopyRoot = isInnerCopyRoot; } @@ -138,20 +142,20 @@ public class SvnRecursiveStatusWalker { return myDepth; } - public SvnStatusClientI getClient() { - return mySvnClient; - } - public SvnStatusClientI getClient(final File file) { - if (! SVNDepth.INFINITY.equals(myDepth)) { - return mySvnClient; + WorkingCopyFormat format = myVcs.getWorkingCopyFormat(file); + + if (format == WorkingCopyFormat.ONE_DOT_EIGHT) { + return myCommandLineClient; } + // check format if (CheckJavaHL.isPresent() && SvnConfiguration.UseAcceleration.javaHL.equals(myConfiguration17.myUseAcceleration) && Svn17Detector.is17(myProject, file)) { return new JavaHLSvnStatusClient(myProject); - } else if (SvnConfiguration.UseAcceleration.commandLine.equals(myConfiguration17.myUseAcceleration) && Svn17Detector.is17(myProject, file)) { - return new SvnCommandLineStatusClient(myProject); + } else if (SvnConfiguration.UseAcceleration.commandLine.equals(myConfiguration17.myUseAcceleration)) { + // apply command line disregarding working copy format + return myCommandLineClient; } return mySvnClient; } @@ -186,7 +190,7 @@ public class SvnRecursiveStatusWalker { return true; } if (file.isDirectory() && new File(file, SVNFileUtil.getAdminDirectoryName()).exists()) { - final MyItem childItem = new MyItem(myProject, path, newDepth, myPartner.createStatusClient(), true); + final MyItem childItem = new MyItem(myVcs, path, newDepth, myPartner.createStatusClient(), true); myQueue.add(childItem); } else if (vf != null) { myReceiver.processUnversioned(vf); @@ -228,12 +232,13 @@ public class SvnRecursiveStatusWalker { } public void checkIfCopyRootWasReported(@Nullable final SVNStatus ioFileStatus, final File ioFile) { - if (! myMetCurrentItem && FileUtil.filesEqual(ioFile, myCurrentItem.getPath().getIOFile())) { + File itemFile = myCurrentItem.getPath().getIOFile(); + if (! myMetCurrentItem && FileUtil.filesEqual(ioFile, itemFile)) { myMetCurrentItem = true; SVNStatus statusInner; try { statusInner = ioFileStatus != null ? ioFileStatus : - myCurrentItem.getClient().doStatus(myCurrentItem.getPath().getIOFile(), false); + myCurrentItem.getClient(itemFile).doStatus(itemFile, false); } catch (SVNException e) { LOG.info(e); @@ -294,7 +299,7 @@ public class SvnRecursiveStatusWalker { //myReceiver.processUnversioned(vFile); //processRecursively(vFile, myCurrentItem.getDepth()); } else { - final MyItem childItem = new MyItem(myProject, new FilePathImpl(vFile), SVNDepth.INFINITY, + final MyItem childItem = new MyItem(myVcs, new FilePathImpl(vFile), SVNDepth.INFINITY, myPartner.createStatusClient(), true); myQueue.add(childItem); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnTestWriteOperationLocks.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnTestWriteOperationLocks.java index 853e995ca219..efea2f93de08 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnTestWriteOperationLocks.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnTestWriteOperationLocks.java @@ -25,6 +25,7 @@ import java.io.File; * Date: 10/23/12 * Time: 2:31 PM */ +// TODO: Used only in SvnLockingTest which is not required anymore. Likely to be removed. public class SvnTestWriteOperationLocks extends SvnAbstractWriteOperationLocks { private final WorkingCopy myWorkingCopy; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java index 0c049ac4671d..7cd6bdd91d3f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java @@ -30,21 +30,19 @@ import com.intellij.openapi.vcs.AbstractVcsHelper; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangesUtil; -import com.intellij.openapi.vcs.impl.ContentRevisionCache; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VfsUtilCore; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.openapi.vfs.*; import com.intellij.openapi.wm.impl.status.StatusBarUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.Convertor; import com.intellij.util.containers.MultiMap; -import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.ClientFactory; import org.jetbrains.idea.svn.branchConfig.SvnBranchConfigurationNew; import org.jetbrains.idea.svn.dialogs.LockDialog; +import org.tmatesoft.sqljet.core.SqlJetException; +import org.tmatesoft.sqljet.core.table.SqlJetDb; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.internal.wc.SVNFileUtil; @@ -53,14 +51,14 @@ import org.tmatesoft.svn.core.io.SVNCapability; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.core.wc2.SvnOperationFactory; +import org.tmatesoft.svn.core.wc2.SvnTarget; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.IOException; -import java.io.OutputStream; import java.util.*; public class SvnUtil { + // TODO: ASP.NET hack behavior should be supported - http://svn.apache.org/repos/asf/subversion/trunk/notes/asp-dot-net-hack.txt + // TODO: Remember this when moving out SVNKit classes. @NonNls public static final String SVN_ADMIN_DIR_NAME = SVNFileUtil.getAdminDirectoryName(); @NonNls public static final String ENTRIES_FILE_NAME = "entries"; @NonNls public static final String WC_DB_FILE_NAME = "wc.db"; @@ -71,13 +69,9 @@ public class SvnUtil { private SvnUtil() { } public static boolean isSvnVersioned(final Project project, File parent) { - try { - final SVNInfo info = SvnVcs.getInstance(project).createWCClient().doInfo(parent, SVNRevision.UNDEFINED); - return info != null; - } - catch (SVNException e) { - return false; - } + final SVNInfo info = SvnVcs.getInstance(project).getInfo(parent); + + return info != null; } public static Collection<VirtualFile> crawlWCRoots(final Project project, File path, SvnWCRootCrawler callback, ProgressIndicator progress) { @@ -123,15 +117,8 @@ public class SvnUtil { @Nullable public static String getExactLocation(final SvnVcs vcs, File path) { - try { - SVNWCClient wcClient = vcs.createWCClient(); - SVNInfo info = wcClient.doInfo(path, SVNRevision.UNDEFINED); - if (info != null && info.getURL() != null) { - return info.getURL().toString(); - } - } - catch (SVNException ignored) { } - return null; + SVNInfo info = vcs.getInfo(path); + return info != null && info.getURL() != null ? info.getURL().toString() : null; } public static Map<String, File> getLocationInfoForModule(final SvnVcs vcs, File path, ProgressIndicator progress) { @@ -288,7 +275,9 @@ public class SvnUtil { } public static String formatRepresentation(final WorkingCopyFormat format) { - if (WorkingCopyFormat.ONE_DOT_SEVEN.equals(format)) { + if (WorkingCopyFormat.ONE_DOT_EIGHT.equals(format)) { + return SvnBundle.message("dialog.show.svn.map.table.version18.text"); + } else if (WorkingCopyFormat.ONE_DOT_SEVEN.equals(format)) { return SvnBundle.message("dialog.show.svn.map.table.version17.text"); } else if (WorkingCopyFormat.ONE_DOT_SIX.equals(format)) { return SvnBundle.message("dialog.show.svn.map.table.version16.text"); @@ -352,6 +341,54 @@ public class SvnUtil { return result; } + /** + * Gets working copy internal format. Works for 1.7 and 1.8. + * + * @param path + * @return + */ + public static WorkingCopyFormat getFormat(final File path) { + int format = 0; + File dbFile = resolveDatabase(path); + + if (dbFile != null) { + SqlJetDb db = null; + try { + db = SqlJetDb.open(dbFile, false); + format = db.getOptions().getUserVersion(); + } + catch (SqlJetException e) { + LOG.error(e); + } finally { + if (db != null) { + try { + db.close(); + } + catch (SqlJetException e) { + LOG.error(e); + } + } + } + } + + return WorkingCopyFormat.getInstance(format); + } + + private static File resolveDatabase(final File path) { + File dbFile = getWcDb(path); + File result = null; + + try { + if (dbFile.exists() && dbFile.isFile()) { + result = dbFile; + } + } catch (SecurityException e) { + LOG.error("Failed to access working copy database", e); + } + + return result; + } + private static class LocationsCrawler implements SvnWCRootCrawler { private final SvnVcs myVcs; private final Map<String, File> myLocations; @@ -371,15 +408,9 @@ public class SvnUtil { oldText = progress.getText(); progress.setText(SvnBundle.message("progress.text.discovering.location", root.getAbsolutePath())); } - try { - SVNWCClient wcClient = myVcs.createWCClient(); - SVNInfo info = wcClient.doInfo(root, SVNRevision.UNDEFINED); - if (info != null && info.getURL() != null) { - myLocations.put(info.getURL().toString(), info.getFile()); - } - } - catch (SVNException e) { - // + SVNInfo info = myVcs.getInfo(root); + if (info != null && info.getURL() != null) { + myLocations.put(info.getURL().toString(), info.getFile()); } if (progress != null) { progress.setText(oldText); @@ -389,20 +420,15 @@ public class SvnUtil { @Nullable public static String getRepositoryUUID(final SvnVcs vcs, final File file) { - final SVNWCClient client = vcs.createWCClient(); - try { - final SVNInfo info = client.doInfo(file, SVNRevision.UNDEFINED); - return (info == null) ? null : info.getRepositoryUUID(); - } catch (SVNException e) { - return null; - } + final SVNInfo info = vcs.getInfo(file); + return info != null ? info.getRepositoryUUID() : null; } @Nullable public static String getRepositoryUUID(final SvnVcs vcs, final SVNURL url) { - final SVNWCClient client = vcs.createWCClient(); try { - final SVNInfo info = client.doInfo(url, SVNRevision.UNDEFINED, SVNRevision.UNDEFINED); + final SVNInfo info = vcs.getInfo(url, SVNRevision.UNDEFINED); + return (info == null) ? null : info.getRepositoryUUID(); } catch (SVNException e) { return null; @@ -411,19 +437,14 @@ public class SvnUtil { @Nullable public static SVNURL getRepositoryRoot(final SvnVcs vcs, final File file) { - final SVNWCClient client = vcs.createWCClient(); - try { - final SVNInfo info = client.doInfo(file, SVNRevision.UNDEFINED); - return (info == null) ? null : info.getRepositoryRootURL(); - } catch (SVNException e) { - return null; - } + final SVNInfo info = vcs.getInfo(file); + return info != null ? info.getRepositoryRootURL() : null; } @Nullable public static SVNURL getRepositoryRoot(final SvnVcs vcs, final String url) { try { - return getRepositoryRoot(vcs, SVNURL.parseURIEncoded(url), true); + return getRepositoryRoot(vcs, SVNURL.parseURIEncoded(url)); } catch (SVNException e) { return null; @@ -431,18 +452,14 @@ public class SvnUtil { } @Nullable - public static SVNURL getRepositoryRoot(final SvnVcs vcs, final SVNURL url, boolean allowRemote) throws SVNException { - final SVNWCClient client = vcs.createWCClient(); - SVNInfo info = client.doInfo(url, SVNRevision.UNDEFINED, SVNRevision.HEAD); + public static SVNURL getRepositoryRoot(final SvnVcs vcs, final SVNURL url) throws SVNException { + SVNInfo info = vcs.getInfo(url, SVNRevision.HEAD); + return (info == null) ? null : info.getRepositoryRootURL(); } public static boolean isWorkingCopyRoot(final File file) { - try { - return SVNWCUtil.isWorkingCopyRoot(file); - } catch (SVNException e) { - return false; - } + return FileUtil.filesEqual(file, getWorkingCopyRootNew(file)); } @Nullable @@ -548,17 +565,9 @@ public class SvnUtil { } public static SVNDepth getDepth(final SvnVcs vcs, final File file) { - final SVNWCClient client = vcs.createWCClient(); - try { - final SVNInfo svnInfo = client.doInfo(file, SVNRevision.UNDEFINED); - if (svnInfo != null) { - return svnInfo.getDepth(); - } - } - catch (SVNException e) { - // - } - return SVNDepth.UNKNOWN; + SVNInfo info = vcs.getInfo(file); + + return info != null && info.getDepth() != null ? info.getDepth() : SVNDepth.UNKNOWN; } public static boolean seemsLikeVersionedDir(final VirtualFile file) { @@ -590,21 +599,17 @@ public class SvnUtil { } public static SVNURL getCommittedURL(final SvnVcs vcs, final File file) { - final File root = getWorkingCopyRoot(file); - if (root == null) return null; - return getUrl(vcs, root); + final File root = getWorkingCopyRootNew(file); + + return root == null ? null : getUrl(vcs, root); } @Nullable public static SVNURL getUrl(final SvnVcs vcs, final File file) { - try { - final SVNInfo info = vcs.createWCClient().doInfo(file, SVNRevision.UNDEFINED); - return info == null ? null : info.getURL(); // todo for moved items? - } - catch (SVNException e) { - LOG.debug(e); - return null; - } + // todo for moved items? + final SVNInfo info = vcs.getInfo(file); + + return info == null ? null : info.getURL(); } public static boolean doesRepositorySupportMergeInfo(final SvnVcs vcs, final SVNURL url) { @@ -648,17 +653,9 @@ public class SvnUtil { @Nullable public static File getWcCopyRootIf17(final File file, @Nullable final File upperBound) { - File current = file; - boolean wcDbFound = false; - while (current != null) { - File wcDb; - if ((wcDb = getWcDb(current)).exists() && ! wcDb.isDirectory()) { - wcDbFound = true; - break; - } - current = current.getParentFile(); - } - if (! wcDbFound) return null; + File current = getParentWithDb(file); + if (current == null) return null; + while (current != null) { try { final SvnWcGeneration svnWcGeneration = SvnOperationFactory.detectWcGeneration(current, false); @@ -674,6 +671,40 @@ public class SvnUtil { return null; } + /** + * Utility method that deals also with 1.8 working copies. + * TODO: Should be renamed when all parts updated for 1.8. + * + * @param file + * @return + */ + @Nullable + public static File getWorkingCopyRootNew(final File file) { + File current = getParentWithDb(file); + if (current == null) return getWorkingCopyRoot(file); + + WorkingCopyFormat format = getFormat(current); + + return WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) || WorkingCopyFormat.ONE_DOT_SEVEN.equals(format) + ? current + : getWorkingCopyRoot(file); + } + + private static File getParentWithDb(File file) { + File current = file; + boolean wcDbFound = false; + while (current != null) { + File wcDb; + if ((wcDb = getWcDb(current)).exists() && ! wcDb.isDirectory()) { + wcDbFound = true; + break; + } + current = current.getParentFile(); + } + if (! wcDbFound) return null; + return current; + } + public static boolean is17CopyPart(final File file) { try { return SvnWcGeneration.V17.equals(SvnOperationFactory.detectWcGeneration(file, true)); @@ -703,44 +734,24 @@ public class SvnUtil { return result; } - public static byte[] getFileContents(final SvnVcs vcs, final String path, final boolean isUrl, final SVNRevision revision, - final SVNRevision pegRevision) + public static byte[] getFileContents(@NotNull final SvnVcs vcs, + @NotNull final SvnTarget target, + @Nullable final SVNRevision revision, + @Nullable final SVNRevision pegRevision) throws VcsException { - final int maxSize = VcsUtil.getMaxVcsLoadedFileSize(); - ByteArrayOutputStream buffer = new ByteArrayOutputStream() { - @Override - public synchronized void write(int b) { - if (size() > maxSize) throw new FileTooBigRuntimeException(); - super.write(b); - } + ClientFactory factory = target.isFile() ? vcs.getFactory(target.getFile()) : vcs.getFactory(); - @Override - public synchronized void write(byte[] b, int off, int len) { - if (size() > maxSize) throw new FileTooBigRuntimeException(); - super.write(b, off, len); - } + return factory.createContentClient().getContent(target, revision, pegRevision); + } - @Override - public synchronized void writeTo(OutputStream out) throws IOException { - if (size() > maxSize) throw new FileTooBigRuntimeException(); - super.writeTo(out); - } - }; - SVNWCClient wcClient = vcs.createWCClient(); + public static SVNURL parseUrl(@NotNull String url) { try { - if (isUrl) { - wcClient.doGetFileContents(SVNURL.parseURIEncoded(path), pegRevision, revision, true, buffer); - } else { - wcClient.doGetFileContents(new File(path), pegRevision, revision, true, buffer); - } - ContentRevisionCache.checkContentsSize(path, buffer.size()); - } catch (FileTooBigRuntimeException e) { - ContentRevisionCache.checkContentsSize(path, buffer.size()); - } catch (SVNException e) { - throw new VcsException(e); + return SVNURL.parseURIEncoded(url); + } + catch (SVNException e) { + IllegalArgumentException runtimeException = new IllegalArgumentException(); + runtimeException.initCause(e); + throw runtimeException; } - return buffer.toByteArray(); } - - private static class FileTooBigRuntimeException extends RuntimeException {} } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java index 748fc86c694c..1a5a0f8b8af7 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - package org.jetbrains.idea.svn; import com.intellij.ide.FrameStateListener; @@ -68,8 +66,12 @@ import org.jetbrains.idea.svn.actions.CleanupWorker; import org.jetbrains.idea.svn.actions.ShowPropertiesDiffWithLocalAction; import org.jetbrains.idea.svn.actions.SvnMergeProvider; import org.jetbrains.idea.svn.annotate.SvnAnnotationProvider; +import org.jetbrains.idea.svn.api.ClientFactory; +import org.jetbrains.idea.svn.api.CmdClientFactory; +import org.jetbrains.idea.svn.api.SvnKitClientFactory; import org.jetbrains.idea.svn.checkin.SvnCheckinEnvironment; import org.jetbrains.idea.svn.checkout.SvnCheckoutProvider; +import org.jetbrains.idea.svn.commandLine.SvnCommandLineInfoClient; import org.jetbrains.idea.svn.commandLine.SvnExecutableChecker; import org.jetbrains.idea.svn.dialogs.SvnBranchPointsCalculator; import org.jetbrains.idea.svn.dialogs.WCInfo; @@ -79,6 +81,7 @@ import org.jetbrains.idea.svn.history.SvnCommittedChangesProvider; import org.jetbrains.idea.svn.history.SvnHistoryProvider; import org.jetbrains.idea.svn.lowLevel.PrimitivePool; import org.jetbrains.idea.svn.networking.SSLProtocolExceptionParser; +import org.jetbrains.idea.svn.portable.SvnWcClientI; import org.jetbrains.idea.svn.rollback.SvnRollbackEnvironment; import org.jetbrains.idea.svn.update.SvnIntegrateEnvironment; import org.jetbrains.idea.svn.update.SvnUpdateEnvironment; @@ -195,9 +198,10 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> { }; private SvnCheckoutProvider myCheckoutProvider; - public void checkCommandLineVersion() { - myChecker.checkExecutableAndNotifyIfNeeded(); - } + private ClientFactory cmdClientFactory; + private ClientFactory svnKitClientFactory; + + private final boolean myLogExceptions; static { System.setProperty("svnkit.log.native.calls", "true"); @@ -235,8 +239,8 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> { public SvnVcs(final Project project, MessageBus bus, SvnConfiguration svnConfiguration, final SvnLoadedBrachesStorage storage) { super(project, VCS_NAME); + myLoadedBranchesStorage = storage; - LOG.debug("ct"); myRootsToWorkingCopies = new RootsToWorkingCopies(this); myConfiguration = svnConfiguration; myAuthNotifier = new SvnAuthenticationNotifier(this); @@ -284,6 +288,9 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> { // remove used some time before old notification group ids correctNotificationIds(); myChecker = new SvnExecutableChecker(myProject); + + Application app = ApplicationManager.getApplication(); + myLogExceptions = app != null && (app.isInternal() || app.isUnitTestMode()); } private void correctNotificationIds() { @@ -348,6 +355,10 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> { }); } + public void checkCommandLineVersion() { + myChecker.checkExecutableAndNotifyIfNeeded(); + } + public void invokeRefreshSvnRoots() { if (REFRESH_LOG.isDebugEnabled()) { REFRESH_LOG.debug("refresh: ", new Throwable()); @@ -481,6 +492,9 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> { myChecker.checkExecutableAndNotifyIfNeeded(); } + cmdClientFactory = new CmdClientFactory(this); + svnKitClientFactory = new SvnKitClientFactory(this); + // do one time after project loaded StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new DumbAwareRunnable() { @Override @@ -911,24 +925,127 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> { return new File(file, pathToDirProps); } + /** + * Provides info either with command line or SvnKit based on project settings. + * Call this method only if failed to detect working copy format by any other means. + * + * @param file + * @return + */ + private SVNInfo runInfoCommand(@NotNull final File file) { + SVNInfo result = null; + + try { + result = SvnConfiguration.UseAcceleration.commandLine.equals(myConfiguration.myUseAcceleration) + ? getInfoCommandLine(file, SVNRevision.UNDEFINED) + : getInfoSvnKit(file); + } + catch (SVNException e) { + handleInfoException(e); + } + + return result; + } + + public SVNInfo getInfo(@NotNull SVNURL url, + SVNRevision pegRevision, + SVNRevision revision, + ISVNAuthenticationManager manager) throws SVNException { + if (SvnConfiguration.UseAcceleration.commandLine.equals(myConfiguration.myUseAcceleration)) { + return createInfoClient().doInfo(url, pegRevision, revision); + } else { + return (manager != null ? createWCClient(manager) : createWCClient()).doInfo(url, pegRevision, revision); + } + } + + public SVNInfo getInfo(@NotNull SVNURL url, SVNRevision revision) throws SVNException { + return getInfo(url, SVNRevision.UNDEFINED, revision, null); + } + @Nullable - public SVNInfo getInfo(final VirtualFile file) { + public SVNInfo getInfo(@NotNull final VirtualFile file) { final File ioFile = new File(file.getPath()); return getInfo(ioFile); } - public SVNInfo getInfo(File ioFile) { + @Nullable + public SVNInfo getInfo(@NotNull String path) { + return getInfo(new File(path)); + } + + @Nullable + public SVNInfo getInfo(@NotNull File ioFile) { + WorkingCopyFormat format = getWorkingCopyFormat(ioFile); + SVNInfo result = null; + try { - SVNWCClient wcClient = createWCClient(); - SVNInfo info = wcClient.doInfo(ioFile, SVNRevision.UNDEFINED); - if (info == null || info.getRepositoryRootURL() == null) { - info = wcClient.doInfo(ioFile, SVNRevision.HEAD); - } - return info; + result = format == WorkingCopyFormat.ONE_DOT_EIGHT ? getInfoCommandLine(ioFile, SVNRevision.UNDEFINED) : runInfoCommand(ioFile); } catch (SVNException e) { - return null; + handleInfoException(e); } + + return result; + } + + @Nullable + public SVNInfo getInfo(@NotNull File ioFile, @NotNull SVNRevision revision) { + WorkingCopyFormat format = getWorkingCopyFormat(ioFile); + SVNInfo result = null; + + try { + result = format == WorkingCopyFormat.ONE_DOT_EIGHT ? getInfoCommandLine(ioFile, revision) : getInfoSvnKit(ioFile, revision); + } + catch (SVNException e) { + handleInfoException(e); + } + + return result; + } + + private void handleInfoException(SVNException e) { + final SVNErrorCode errorCode = e.getErrorMessage().getErrorCode(); + if (!myLogExceptions || + SVNErrorCode.WC_PATH_NOT_FOUND.equals(errorCode) || + SVNErrorCode.UNVERSIONED_RESOURCE.equals(errorCode) || + SVNErrorCode.WC_NOT_WORKING_COPY.equals(errorCode)) { + LOG.debug(e); + } + else { + LOG.error(e); + } + } + + private SVNInfo getInfoSvnKit(@NotNull File ioFile) throws SVNException { + SVNInfo info = getInfoSvnKit(ioFile, SVNRevision.UNDEFINED); + if (info == null || info.getRepositoryRootURL() == null) { + info = getInfoSvnKit(ioFile, SVNRevision.HEAD); + } + return info; + } + + private SVNInfo getInfoSvnKit(@NotNull File ioFile, SVNRevision revision) throws SVNException { + return createWCClient().doInfo(ioFile, revision); + } + + private SVNInfo getInfoCommandLine(@NotNull File ioFile, SVNRevision revision) throws SVNException { + SvnCommandLineInfoClient client = new SvnCommandLineInfoClient(myProject); + return client.doInfo(ioFile, revision); + } + + private SvnWcClientI createInfoClient() { + return new SvnCommandLineInfoClient(myProject); + } + + public WorkingCopyFormat getWorkingCopyFormat(@NotNull File ioFile) { + RootUrlInfo rootInfo = getSvnFileUrlMapping().getWcRootForFilePath(ioFile); + WorkingCopyFormat format = rootInfo != null ? rootInfo.getFormat() : WorkingCopyFormat.UNKNOWN; + + if (WorkingCopyFormat.UNKNOWN.equals(format)) { + format = SvnFormatSelector.findRootAndGetFormat(ioFile); + } + + return format; } public void refreshSSLProperty() { @@ -1245,4 +1362,27 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> { } return myCheckoutProvider; } + + /** + * Try to avoid usages of this method (for now) as it could not correctly for all cases + * detect svn 1.8 working copy format to guarantee command line client. + * + * For instance, when working copies of several formats are presented in project + * (though it seems to be rather unlikely case). + * + * @return + */ + public ClientFactory getFactory() { + // check working copy format of project directory + WorkingCopyFormat format = getWorkingCopyFormat(new File(getProject().getBaseDir().getPath())); + + return WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) || + myConfiguration.myUseAcceleration.equals(SvnConfiguration.UseAcceleration.commandLine) ? cmdClientFactory : svnKitClientFactory; + } + + public ClientFactory getFactory(@NotNull File file) { + WorkingCopyFormat format = getWorkingCopyFormat(file); + + return WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) ? cmdClientFactory : getFactory(); + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnWriteOperationLocks.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnWriteOperationLocks.java index 36e0b08c5a86..ca97edb1c228 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnWriteOperationLocks.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnWriteOperationLocks.java @@ -30,6 +30,7 @@ import java.io.File; * Date: 10/23/12 * Time: 2:29 PM */ +// TODO: Such locking functionality is not required anymore. Likely to be removed. public class SvnWriteOperationLocks extends SvnAbstractWriteOperationLocks { private final RootsToWorkingCopies myRootsToWorkingCopies; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/WorkingCopyFormat.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/WorkingCopyFormat.java index d40f947a71ee..c007541816c5 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/WorkingCopyFormat.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/WorkingCopyFormat.java @@ -15,19 +15,22 @@ */ package org.jetbrains.idea.svn; -import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb; - /** * since not all constants are available from svnkit & constants are fixed */ public enum WorkingCopyFormat { + ONE_DOT_THREE(4, false, false, false, SvnBundle.message("dialog.show.svn.map.table.version13.text")), ONE_DOT_FOUR(8, false, false, false, SvnBundle.message("dialog.show.svn.map.table.version14.text")), ONE_DOT_FIVE(9, true, true, false, SvnBundle.message("dialog.show.svn.map.table.version15.text")), ONE_DOT_SIX(10, true, true, true, SvnBundle.message("dialog.show.svn.map.table.version16.text")), ONE_DOT_SEVEN(12, true, true, true, SvnBundle.message("dialog.show.svn.map.table.version17.text")), + ONE_DOT_EIGHT(12, true, true, true, SvnBundle.message("dialog.show.svn.map.table.version18.text")), UNKNOWN(0, false, false, false, "unknown"); + public static final int INTERNAL_FORMAT_17 = 29; + public static final int INTERNAL_FORMAT_18 = 31; + private final int myFormat; private final boolean myChangelistSupport; private final boolean myMergeInfoSupport; @@ -60,10 +63,11 @@ public enum WorkingCopyFormat { public static WorkingCopyFormat getInstance(final int value) { // somewhy 1.7 wc format can also be 29 - if (ISVNWCDb.WC_FORMAT_17 == value) { + if (INTERNAL_FORMAT_17 == value) { return ONE_DOT_SEVEN; - } - if (ONE_DOT_FIVE.getFormat() == value) { + } else if (INTERNAL_FORMAT_18 == value) { + return ONE_DOT_EIGHT; + } else if (ONE_DOT_FIVE.getFormat() == value) { return ONE_DOT_FIVE; } else if (ONE_DOT_FOUR.getFormat() == value) { return ONE_DOT_FOUR; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AddAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AddAction.java index 06c255b6ed38..7558a483fcd7 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AddAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AddAction.java @@ -30,8 +30,6 @@ import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnStatusUtil; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.checkin.SvnCheckinEnvironment; -import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.wc.SVNWCClient; import java.util.*; @@ -68,8 +66,6 @@ public class AddAction extends BasicAction { ProjectLevelVcsManager manager = ProjectLevelVcsManager.getInstance(project); manager.startBackgroundVcsOperation(); try { - - SVNWCClient wcClient = activeVcs.createWCClient(); final Set<VirtualFile> additionallyDirty = new HashSet<VirtualFile>(); final FileStatusManager fileStatusManager = FileStatusManager.getInstance(project); for (VirtualFile item : items) { @@ -84,13 +80,13 @@ public class AddAction extends BasicAction { } } } - Collection<SVNException> exceptions = - SvnCheckinEnvironment.scheduleUnversionedFilesForAddition(wcClient, Arrays.asList(items), true); + Collection<VcsException> exceptions = + SvnCheckinEnvironment.scheduleUnversionedFilesForAddition(activeVcs, Arrays.asList(items), true); additionallyDirty.addAll(Arrays.asList(items)); markDirty(project, additionallyDirty); if (!exceptions.isEmpty()) { final Collection<String> messages = new ArrayList<String>(exceptions.size()); - for (SVNException exception : exceptions) { + for (VcsException exception : exceptions) { messages.add(exception.getMessage()); } throw new VcsException(messages); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkResolvedAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkResolvedAction.java index 87b36970228b..d65c6d3c9c20 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkResolvedAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkResolvedAction.java @@ -19,6 +19,7 @@ package org.jetbrains.idea.svn.actions; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vcs.AbstractVcs; @@ -32,8 +33,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnStatusUtil; import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.conflict.ConflictClient; import org.jetbrains.idea.svn.dialogs.SelectFilesDialog; -import org.tmatesoft.svn.core.SVNDepth; +import org.jetbrains.idea.svn.portable.SvnStatusClientI; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.wc.*; @@ -42,6 +44,8 @@ import java.util.Collection; import java.util.TreeSet; public class MarkResolvedAction extends BasicAction { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.actions.MarkResolvedAction"); + protected String getActionName(AbstractVcs vcs) { return SvnBundle.message("action.name.mark.resolved"); } @@ -88,15 +92,13 @@ public class MarkResolvedAction extends BasicAction { } pathsArray = dialog.getSelectedPaths(); try { - SVNWCClient wcClient = vcs.createWCClient(); for (String path : pathsArray) { File ioFile = new File(path); - wcClient.doResolve(ioFile, SVNDepth.EMPTY, SVNConflictChoice.MERGED); + ConflictClient client = vcs.getFactory(ioFile).createConflictClient(); + + client.resolve(ioFile, true); } } - catch (SVNException e) { - throw new VcsException(e); - } finally { for (VirtualFile file : files) { VcsDirtyScopeManager.getInstance(project).fileDirty(file); @@ -115,10 +117,12 @@ public class MarkResolvedAction extends BasicAction { private static Collection<String> collectResolvablePaths(final SvnVcs vcs, VirtualFile[] files) { final Collection<String> target = new TreeSet<String>(); - SVNStatusClient stClient = vcs.createStatusClient(); for (VirtualFile file : files) { try { - stClient.doStatus(new File(file.getPath()), true, false, false, false, new ISVNStatusHandler() { + File path = new File(file.getPath()); + SvnStatusClientI client = vcs.getFactory(path).createStatusClient(); + + client.doStatus(path, true, false, false, false, new ISVNStatusHandler() { public void handleStatus(SVNStatus status) { if (status.getContentsStatus() == SVNStatusType.STATUS_CONFLICTED || status.getPropertiesStatus() == SVNStatusType.STATUS_CONFLICTED) { @@ -128,7 +132,7 @@ public class MarkResolvedAction extends BasicAction { }); } catch (SVNException e) { - // + LOG.warn(e); } } return target; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java index 4c582d01557d..3130dc948c13 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java @@ -27,12 +27,13 @@ import com.intellij.vcsUtil.VcsRunnable; import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnRevisionNumber; +import org.jetbrains.idea.svn.SvnUtil; import org.jetbrains.idea.svn.SvnVcs; -import org.tmatesoft.svn.core.SVNDepth; -import org.tmatesoft.svn.core.SVNException; +import org.jetbrains.idea.svn.properties.PropertyClient; import org.tmatesoft.svn.core.SVNProperty; import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.wc.*; +import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.ByteArrayOutputStream; import java.io.File; @@ -57,38 +58,34 @@ public class SvnMergeProvider implements MergeProvider { final MergeData data = new MergeData(); VcsRunnable runnable = new VcsRunnable() { public void run() throws VcsException { - SvnVcs vcs = SvnVcs.getInstance(myProject); File oldFile = null; File newFile = null; File workingFile = null; - SVNWCClient client; boolean mergeCase = false; - try { - client = vcs.createWCClient(); - SVNInfo info = client.doInfo(new File(file.getPath()), SVNRevision.UNDEFINED); - if (info != null) { - oldFile = info.getConflictOldFile(); - newFile = info.getConflictNewFile(); + SvnVcs vcs = SvnVcs.getInstance(myProject); + SVNInfo info = vcs.getInfo(file); + + if (info != null) { + oldFile = info.getConflictOldFile(); + newFile = info.getConflictNewFile(); + workingFile = info.getConflictWrkFile(); + mergeCase = workingFile == null || workingFile.getName().contains("working"); + // for debug + if (workingFile == null) { + LOG.info("Null working file when merging text conflict for " + file.getPath() + " old file: " + oldFile + " new file: " + newFile); + } + if (mergeCase) { + // this is merge case + oldFile = info.getConflictNewFile(); + newFile = info.getConflictOldFile(); workingFile = info.getConflictWrkFile(); - mergeCase = workingFile == null || workingFile.getName().contains("working"); - // for debug - if (workingFile == null) { - LOG.info("Null working file when merging text conflict for " + file.getPath() + " old file: " + oldFile + " new file: " + newFile); - } - if (mergeCase) { - // this is merge case - oldFile = info.getConflictNewFile(); - newFile = info.getConflictOldFile(); - workingFile = info.getConflictWrkFile(); - } - data.LAST_REVISION_NUMBER = new SvnRevisionNumber(info.getRevision()); } - } - catch (SVNException e) { - throw new VcsException(e); + data.LAST_REVISION_NUMBER = new SvnRevisionNumber(info.getRevision()); + } else { + throw new VcsException("Could not get info for " + file.getPath()); } if (oldFile == null || newFile == null || workingFile == null) { - ByteArrayOutputStream bos = getBaseRevisionContents(client, file); + ByteArrayOutputStream bos = getBaseRevisionContents(vcs, file); data.ORIGINAL = bos.toByteArray(); data.LAST = bos.toByteArray(); data.CURRENT = readFile(new File(file.getPath())); @@ -99,7 +96,7 @@ public class SvnMergeProvider implements MergeProvider { data.CURRENT = readFile(workingFile); } if (mergeCase) { - final ByteArrayOutputStream contents = getBaseRevisionContents(vcs.createWCClient(), file); + final ByteArrayOutputStream contents = getBaseRevisionContents(vcs, file); if (! Arrays.equals(contents.toByteArray(), data.ORIGINAL)) { // swap base and server: another order of merge arguments byte[] original = data.ORIGINAL; @@ -114,13 +111,17 @@ public class SvnMergeProvider implements MergeProvider { return data; } - private ByteArrayOutputStream getBaseRevisionContents(SVNWCClient client, VirtualFile file) { + private ByteArrayOutputStream getBaseRevisionContents(@NotNull SvnVcs vcs, @NotNull VirtualFile file) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { - client.doGetFileContents(new File(file.getPath()), SVNRevision.UNDEFINED, SVNRevision.BASE, true, bos); + byte[] contents = SvnUtil.getFileContents(vcs, SvnTarget.fromFile(new File(file.getPath())), SVNRevision.BASE, SVNRevision.UNDEFINED); + bos.write(contents); } - catch (SVNException e) { - // + catch (VcsException e) { + LOG.warn(e); + } + catch (IOException e) { + LOG.warn(e); } return bos; } @@ -135,13 +136,14 @@ public class SvnMergeProvider implements MergeProvider { } public void conflictResolvedForFile(VirtualFile file) { + // TODO: Add possibility to resolve content conflicts separately from property conflicts. SvnVcs vcs = SvnVcs.getInstance(myProject); + File path = new File(file.getPath()); try { - SVNWCClient client = vcs.createWCClient(); - client.doResolve(new File(file.getPath()), SVNDepth.EMPTY, true, false, SVNConflictChoice.MERGED); + vcs.getFactory(path).createConflictClient().resolve(path, false); } - catch (SVNException e) { - // + catch (VcsException e) { + LOG.warn(e); } // the .mine/.r## files have been deleted final VirtualFile parent = file.getParent(); @@ -152,17 +154,20 @@ public class SvnMergeProvider implements MergeProvider { public boolean isBinary(@NotNull final VirtualFile file) { SvnVcs vcs = SvnVcs.getInstance(myProject); + try { - SVNWCClient client = vcs.createWCClient(); File ioFile = new File(file.getPath()); - SVNPropertyData svnPropertyData = client.doGetProperty(ioFile, SVNProperty.MIME_TYPE, SVNRevision.UNDEFINED, SVNRevision.WORKING); + PropertyClient client = vcs.getFactory(ioFile).createPropertyClient(); + + SVNPropertyData svnPropertyData = client.getProperty(ioFile, SVNProperty.MIME_TYPE, false, SVNRevision.UNDEFINED, SVNRevision.WORKING); if (svnPropertyData != null && SVNProperty.isBinaryMimeType(SVNPropertyValue.getPropertyAsString(svnPropertyData.getValue()))) { return true; } } - catch (SVNException e) { - // + catch (VcsException e) { + LOG.warn(e); } + return false; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/add/AddClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/AddClient.java new file mode 100644 index 000000000000..718a56dea9af --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/AddClient.java @@ -0,0 +1,23 @@ +package org.jetbrains.idea.svn.add; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface AddClient extends SvnClient { + + void add(@NotNull File file, + @Nullable SVNDepth depth, + boolean makeParents, + boolean includeIgnored, + boolean force, + @Nullable ISVNEventHandler handler) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/add/CmdAddClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/CmdAddClient.java new file mode 100644 index 000000000000..c7866713c9c1 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/CmdAddClient.java @@ -0,0 +1,69 @@ +package org.jetbrains.idea.svn.add; + +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.containers.Convertor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.api.FileStatusResultParser; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNEvent; +import org.tmatesoft.svn.core.wc.SVNStatusType; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdAddClient extends BaseSvnClient implements AddClient { + + private static final String STATUS = "\\s*(\\w)\\s*"; + private static final String OPTIONAL_FILE_TYPE = "(\\(.*\\))?"; + private static final String PATH = "\\s*(.*?)\\s*"; + private static final Pattern CHANGED_PATH = Pattern.compile(STATUS + OPTIONAL_FILE_TYPE + PATH); + + @Override + public void add(@NotNull File file, + @Nullable SVNDepth depth, + boolean makeParents, + boolean includeIgnored, + boolean force, + @Nullable ISVNEventHandler handler) throws VcsException { + List<String> parameters = prepareParameters(file, depth, makeParents, includeIgnored, force); + + // TODO: handler should be called in parallel with command execution, but this will be in other thread + // TODO: check if that is ok for current handler implementation + // TODO: add possibility to invoke "handler.checkCancelled" - process should be killed + CommandUtil.execute(myVcs, SvnCommandName.add, parameters, new FileStatusResultParser(CHANGED_PATH, handler, new AddStatusConvertor())); + } + + private static List<String> prepareParameters(File file, SVNDepth depth, boolean makeParents, boolean includeIgnored, boolean force) { + List<String> parameters = new ArrayList<String>(); + + CommandUtil.put(parameters, file); + CommandUtil.put(parameters, depth); + CommandUtil.put(parameters, makeParents, "--parents"); + CommandUtil.put(parameters, includeIgnored, "--no-ignore"); + CommandUtil.put(parameters, force, "--force"); + + return parameters; + } + + private static class AddStatusConvertor implements Convertor<Matcher, SVNEvent> { + @Override + public SVNEvent convert(Matcher o) { + SVNStatusType contentStatus = CommandUtil.getStatusType(o.group(1)); + String path = o.group(3); + + return new SVNEvent(new File(path), null, null, 0, contentStatus, null, null, null, null, null, null, null, + null, null, null); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/add/SvnKitAddClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/SvnKitAddClient.java new file mode 100644 index 000000000000..d6250c71f073 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/SvnKitAddClient.java @@ -0,0 +1,39 @@ +package org.jetbrains.idea.svn.add; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNWCClient; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitAddClient extends BaseSvnClient implements AddClient { + + @Override + public void add(@NotNull File file, + @Nullable SVNDepth depth, + boolean makeParents, + boolean includeIgnored, + boolean force, + @Nullable ISVNEventHandler handler) throws VcsException { + try { + SVNWCClient client = myVcs.createWCClient(); + + client.setEventHandler(handler); + client.doAdd(file, force, + false, // directory should already be created + makeParents, // not used but will be passed as makeParents value + SVNDepth.recurseFromDepth(depth)); + } + catch (SVNException e) { + throw new VcsException(e); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/AnnotateClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/AnnotateClient.java new file mode 100644 index 000000000000..df30cfd89ae2 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/AnnotateClient.java @@ -0,0 +1,24 @@ +package org.jetbrains.idea.svn.annotate; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.wc.ISVNAnnotateHandler; +import org.tmatesoft.svn.core.wc.SVNDiffOptions; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +/** + * @author Konstantin Kolosovsky. + */ +public interface AnnotateClient extends SvnClient { + + void annotate(@NotNull SvnTarget target, + @NotNull SVNRevision startRevision, + @NotNull SVNRevision endRevision, + @Nullable SVNRevision pegRevision, + boolean includeMergedRevisions, + @Nullable SVNDiffOptions diffOptions, + @Nullable ISVNAnnotateHandler handler) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/CmdAnnotateClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/CmdAnnotateClient.java new file mode 100644 index 000000000000..d29d40eca84a --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/CmdAnnotateClient.java @@ -0,0 +1,126 @@ +package org.jetbrains.idea.svn.annotate; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommand; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.ISVNAnnotateHandler; +import org.tmatesoft.svn.core.wc.SVNDiffOptions; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdAnnotateClient extends BaseSvnClient implements AnnotateClient { + + @Override + public void annotate(@NotNull SvnTarget target, + @NotNull SVNRevision startRevision, + @NotNull SVNRevision endRevision, + @Nullable SVNRevision pegRevision, + boolean includeMergedRevisions, + @Nullable SVNDiffOptions diffOptions, + @Nullable final ISVNAnnotateHandler handler) throws VcsException { + // TODO: after merge remove setting includeMergedRevisions to false and update parsing + includeMergedRevisions = false; + + List<String> parameters = new ArrayList<String>(); + CommandUtil.put(parameters, target.getPathOrUrlString(), pegRevision); + parameters.add("--revision"); + parameters.add(startRevision + ":" + endRevision); + CommandUtil.put(parameters, includeMergedRevisions, "--use-merge-history"); + CommandUtil.put(parameters, diffOptions); + parameters.add("--xml"); + + SvnCommand command = CommandUtil.execute(myVcs, SvnCommandName.blame, parameters, null); + + parseOutput(command.getOutput(), handler); + } + + public void parseOutput(@NotNull String output, @Nullable ISVNAnnotateHandler handler) throws VcsException { + try { + JAXBContext context = JAXBContext.newInstance(BlameInfo.class); + Unmarshaller unmarshaller = context.createUnmarshaller(); + BlameInfo info = (BlameInfo)unmarshaller.unmarshal(new StringReader(output)); + + if (handler != null && info != null && info.target != null && info.target.lineEntries != null) { + for (LineEntry entry : info.target.lineEntries) { + invokeHandler(handler, entry); + } + } + } + catch (JAXBException e) { + throw new VcsException(e); + } + catch (SVNException e) { + throw new VcsException(e); + } + } + + private static void invokeHandler(ISVNAnnotateHandler handler, LineEntry entry) throws SVNException { + // line numbers in our api start from 0 - not from 1 like in svn output + handler.handleLine(entry.date(), entry.revision(), entry.author(), null, null, 0, null, null, entry.lineNumber - 1); + } + + @XmlRootElement(name = "blame") + public static class BlameInfo { + + @XmlElement(name = "target") + public TargetEntry target; + } + + public static class TargetEntry { + + @XmlElement(name = "entry") + List<LineEntry> lineEntries; + } + + public static class LineEntry { + + @XmlAttribute(name = "line-number") + public int lineNumber; + + @XmlElement(name = "commit") + public CommitEntry commit; + + public long revision() { + return commit != null ? commit.revision : 0; + } + + public String author() { + return commit != null ? commit.author : null; + } + + public Date date() { + return commit != null ? commit.date : null; + } + } + + public static class CommitEntry { + + @XmlAttribute(name = "revision") + public long revision; + + @XmlElement(name = "author") + public String author; + + @XmlElement(name = "date") + public Date date; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnAnnotationProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnAnnotationProvider.java index d0885c8a293e..88adf25e401c 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnAnnotationProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnAnnotationProvider.java @@ -32,14 +32,16 @@ import com.intellij.openapi.vcs.history.*; import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; +import org.jetbrains.idea.svn.history.HistoryClient; import org.jetbrains.idea.svn.history.SvnChangeList; import org.jetbrains.idea.svn.history.SvnFileRevision; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.*; +import org.tmatesoft.svn.core.wc2.SvnTarget; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; @@ -89,9 +91,8 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn final String contents; if (loadExternally) { - final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - myVcs.createWCClient().doGetFileContents(ioFile, SVNRevision.UNDEFINED, SVNRevision.BASE, true, buffer); - contents = LoadTextUtil.getTextByBinaryPresentation(buffer.toByteArray(), file, false, false).toString(); + byte[] data = SvnUtil.getFileContents(myVcs, SvnTarget.fromFile(ioFile), SVNRevision.BASE, SVNRevision.UNDEFINED); + contents = LoadTextUtil.getTextByBinaryPresentation(data, file, false, false).toString(); } else { final byte[] bytes = VcsHistoryUtil.loadRevisionContent(revision); contents = LoadTextUtil.getTextByBinaryPresentation(bytes, file, false, false).toString(); @@ -99,16 +100,13 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn final SvnFileAnnotation result = new SvnFileAnnotation(myVcs, file, contents, lastChangedRevision); - SVNWCClient wcClient = myVcs.createWCClient(); - info = wcClient.doInfo(ioFile, SVNRevision.UNDEFINED); + info = myVcs.getInfo(ioFile); if (info == null) { exception[0] = new VcsException(new SVNException(SVNErrorMessage.create(SVNErrorCode.UNKNOWN, "File ''{0}'' is not under version control", ioFile))); return; } final String url = info.getURL() == null ? null : info.getURL().toString(); - SVNLogClient client = myVcs.createLogClient(); - setLogClientOptions(client); SVNRevision endRevision = ((SvnFileRevision) revision).getRevision(); if (SVNRevision.WORKING.equals(endRevision)) { endRevision = info.getRevision(); @@ -122,14 +120,20 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn final boolean calculateMergeinfo = SvnConfiguration.getInstance(myVcs.getProject()).SHOW_MERGE_SOURCES_IN_ANNOTATE && SvnUtil.checkRepositoryVersion15(myVcs, url); - final SVNRevision svnRevision = ((SvnRevisionNumber)revision.getRevisionNumber()).getRevision(); + final MySteppedLogGetter logGetter = new MySteppedLogGetter( + myVcs, ioFile, progress, + myVcs.getFactory(ioFile).createHistoryClient(), endRevision, result, + url, calculateMergeinfo, file.getCharset()); - final MySteppedLogGetter logGetter = new MySteppedLogGetter(myVcs, ioFile, progress, client, endRevision, result, url, calculateMergeinfo, file.getCharset()); logGetter.go(); final LinkedList<SVNRevision> rp = logGetter.getRevisionPoints(); + // TODO: only 2 elements will be in rp and for loop will be executed only once - probably rewrite with Pair + AnnotateClient annotateClient = myVcs.getFactory(ioFile).createAnnotateClient(); for (int i = 0; i < rp.size() - 1; i++) { - client.doAnnotate(ioFile, svnRevision, rp.get(i + 1), rp.get(i), true, calculateMergeinfo, annotateHandler, null); + annotateClient.annotate(SvnTarget.fromFile(ioFile), rp.get(i + 1), rp.get(i), ((SvnFileRevision)revision).getPegRevision(), + calculateMergeinfo, + getLogClientOptions(myVcs), annotateHandler); } if (rp.get(1).getNumber() > 0) { @@ -137,31 +141,15 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn } annotation[0] = result; } - catch (SVNException e) { - if (SVNErrorCode.FS_NOT_FOUND.equals(e.getErrorMessage().getErrorCode())) { - final CommittedChangesProvider<SvnChangeList,ChangeBrowserSettings> provider = myVcs.getCommittedChangesProvider(); - try { - final Pair<SvnChangeList, FilePath> pair = provider.getOneList(file, revision.getRevisionNumber()); - if (pair != null && info != null && pair.getSecond() != null && ! Comparing.equal(pair.getSecond().getIOFile(), ioFile)) { - annotation[0] = annotateNonExisting(pair, revision, info, file.getCharset(), file); - return; - } - } - catch (VcsException e1) { - exception[0] = e1; - } - catch (SVNException e1) { - exception[0] = new VcsException(e); - } - catch (IOException e1) { - exception[0] = new VcsException(e); - } - } - exception[0] = new VcsException(e); - } catch (IOException e) { + catch (IOException e) { exception[0] = new VcsException(e); } catch (VcsException e) { - exception[0] = e; + if (e.getCause() instanceof SVNException) { + handleSvnException(ioFile, info, (SVNException)e.getCause(), file, revision, annotation, exception); + } + else { + exception[0] = e; + } } } }; @@ -177,6 +165,35 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn return annotation[0]; } + private void handleSvnException(File ioFile, + SVNInfo info, + SVNException e, + VirtualFile file, + VcsFileRevision revision, + FileAnnotation[] annotation, VcsException[] exception) { + // TODO: Check how this scenario could be reproduced by user and what changes needs to be done for command line client + if (SVNErrorCode.FS_NOT_FOUND.equals(e.getErrorMessage().getErrorCode())) { + final CommittedChangesProvider<SvnChangeList,ChangeBrowserSettings> provider = myVcs.getCommittedChangesProvider(); + try { + final Pair<SvnChangeList, FilePath> pair = provider.getOneList(file, revision.getRevisionNumber()); + if (pair != null && info != null && pair.getSecond() != null && ! Comparing.equal(pair.getSecond().getIOFile(), ioFile)) { + annotation[0] = annotateNonExisting(pair, revision, info, file.getCharset(), file); + return; + } + } + catch (VcsException e1) { + exception[0] = e1; + } + catch (SVNException e1) { + exception[0] = new VcsException(e); + } + catch (IOException e1) { + exception[0] = new VcsException(e); + } + } + exception[0] = new VcsException(e); + } + public static File getCommonAncestor(final File file1, final File file2) throws IOException { if (FileUtil.filesEqual(file1, file2)) return file1; final File can1 = file1.getCanonicalFile(); @@ -214,8 +231,7 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn final String relativePath = FileUtil.getRelativePath(root.getPath(), wasFile.getPath(), File.separatorChar); if (relativePath == null) throw new VcsException("Can not find relative path for " + wasFile.getPath() + "@" + revision.getRevisionNumber().asString()); - SVNWCClient wcClient = myVcs.createWCClient(); - SVNInfo wcRootInfo = wcClient.doInfo(root, SVNRevision.UNDEFINED); + SVNInfo wcRootInfo = myVcs.getInfo(root); if (wcRootInfo == null || wcRootInfo.getURL() == null) { throw new VcsException("Can not find relative path for " + wasFile.getPath() + "@" + revision.getRevisionNumber().asString()); } @@ -225,21 +241,18 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn wasUrl = wasUrl.appendPath(string, true); } - final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); final SVNRevision svnRevision = ((SvnRevisionNumber)revision.getRevisionNumber()).getRevision(); - myVcs.createWCClient().doGetFileContents(wasUrl, svnRevision, svnRevision, true, buffer); - final String contents = LoadTextUtil.getTextByBinaryPresentation(buffer.toByteArray(), - charset == null ? CharsetToolkit.UTF8_CHARSET : charset).toString(); - SVNLogClient client = myVcs.createLogClient(); - setLogClientOptions(client); + byte[] data = SvnUtil.getFileContents(myVcs, SvnTarget.fromURL(wasUrl), svnRevision, svnRevision); + final String contents = LoadTextUtil.getTextByBinaryPresentation(data, charset == null ? CharsetToolkit.UTF8_CHARSET : charset).toString(); final SvnRemoteFileAnnotation result = new SvnRemoteFileAnnotation(myVcs, contents, revision.getRevisionNumber(), pair.getFirst(), pair.getSecond().getPath(), current); final ISVNAnnotateHandler annotateHandler = createAnnotationHandler(ProgressManager.getInstance().getProgressIndicator(), result); final boolean calculateMergeinfo = SvnConfiguration.getInstance(myVcs.getProject()).SHOW_MERGE_SOURCES_IN_ANNOTATE && SvnUtil.checkRepositoryVersion15(myVcs, wasUrl.toString()); - client.doAnnotate(wasUrl, svnRevision, SVNRevision.create(1), svnRevision, true, calculateMergeinfo, annotateHandler, null); - + AnnotateClient client = myVcs.getFactory().createAnnotateClient(); + client.annotate(SvnTarget.fromURL(wasUrl), SVNRevision.create(1), svnRevision, svnRevision, calculateMergeinfo, + getLogClientOptions(myVcs), annotateHandler); return result; } @@ -370,14 +383,14 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn private final SvnVcs myVcs; private final File myIoFile; private final ProgressIndicator myProgress; - private final SVNLogClient myClient; + private final HistoryClient myClient; private final SVNRevision myEndRevision; private final boolean myCalculateMergeinfo; private final SvnFileAnnotation myResult; private final String myUrl; private final Charset myCharset; - private MySteppedLogGetter(final SvnVcs vcs, final File ioFile, final ProgressIndicator progress, final SVNLogClient client, + private MySteppedLogGetter(final SvnVcs vcs, final File ioFile, final ProgressIndicator progress, final HistoryClient client, final SVNRevision endRevision, final SvnFileAnnotation result, final String url, @@ -395,7 +408,7 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn myRevisionPoints = new LinkedList<SVNRevision>(); } - public void go() throws SVNException { + public void go() throws VcsException { final int maxAnnotateRevisions = SvnConfiguration.getInstance(myVcs.getProject()).getMaxAnnotateRevisions(); boolean longHistory = true; if (maxAnnotateRevisions == -1) { @@ -437,8 +450,8 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn myRevisionPoints.add(SVNRevision.create(0)); } - private void doLog(final boolean includeMerged, final SVNRevision truncateTo, final int max) throws SVNException { - myClient.doLog(new File[]{myIoFile}, myEndRevision, truncateTo == null ? SVNRevision.create(1L) : truncateTo, + private void doLog(final boolean includeMerged, final SVNRevision truncateTo, final int max) throws VcsException { + myClient.doLog(myIoFile, myEndRevision, truncateTo == null ? SVNRevision.create(1L) : truncateTo, SVNRevision.UNDEFINED, false, false, includeMerged, max, null, new ISVNLogEntryHandler() { public void handleLogEntry(SVNLogEntry logEntry) { @@ -464,9 +477,7 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn return true; } - private void setLogClientOptions(final SVNLogClient client) { - if (SvnConfiguration.getInstance(myVcs.getProject()).IGNORE_SPACES_IN_ANNOTATE) { - client.setDiffOptions(new SVNDiffOptions(true, true, true)); - } + private static SVNDiffOptions getLogClientOptions(@NotNull SvnVcs vcs) { + return SvnConfiguration.getInstance(vcs.getProject()).IGNORE_SPACES_IN_ANNOTATE ? new SVNDiffOptions(true, true, true) : null; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnKitAnnotateClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnKitAnnotateClient.java new file mode 100644 index 000000000000..315178c9079e --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnKitAnnotateClient.java @@ -0,0 +1,42 @@ +package org.jetbrains.idea.svn.annotate; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.ISVNAnnotateHandler; +import org.tmatesoft.svn.core.wc.SVNDiffOptions; +import org.tmatesoft.svn.core.wc.SVNLogClient; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitAnnotateClient extends BaseSvnClient implements AnnotateClient { + + @Override + public void annotate(@NotNull SvnTarget target, + @NotNull SVNRevision startRevision, + @NotNull SVNRevision endRevision, + @Nullable SVNRevision pegRevision, + boolean includeMergedRevisions, + @Nullable SVNDiffOptions diffOptions, + @Nullable ISVNAnnotateHandler handler) throws VcsException { + try { + SVNLogClient client = myVcs.createLogClient(); + + client.setDiffOptions(diffOptions); + if (target.isFile()) { + client.doAnnotate(target.getFile(), pegRevision, startRevision, endRevision, true, includeMergedRevisions, handler, null); + } + else { + client.doAnnotate(target.getURL(), pegRevision, startRevision, endRevision, true, includeMergedRevisions, handler, null); + } + } + catch (SVNException e) { + throw new VcsException(e); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/BaseSvnClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/BaseSvnClient.java new file mode 100644 index 000000000000..54401cff68b1 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/BaseSvnClient.java @@ -0,0 +1,22 @@ +package org.jetbrains.idea.svn.api; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnVcs; + +/** + * @author Konstantin Kolosovsky. + */ +public abstract class BaseSvnClient implements SvnClient { + protected SvnVcs myVcs; + + @NotNull + @Override + public SvnVcs getVcs() { + return myVcs; + } + + @Override + public void setVcs(@NotNull SvnVcs vcs) { + myVcs = vcs; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/ClientFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/ClientFactory.java new file mode 100644 index 000000000000..b85121637e6a --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/ClientFactory.java @@ -0,0 +1,99 @@ +package org.jetbrains.idea.svn.api; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.add.AddClient; +import org.jetbrains.idea.svn.annotate.AnnotateClient; +import org.jetbrains.idea.svn.conflict.ConflictClient; +import org.jetbrains.idea.svn.content.ContentClient; +import org.jetbrains.idea.svn.copy.CopyMoveClient; +import org.jetbrains.idea.svn.delete.DeleteClient; +import org.jetbrains.idea.svn.history.HistoryClient; +import org.jetbrains.idea.svn.portable.SvnStatusClientI; +import org.jetbrains.idea.svn.properties.PropertyClient; +import org.jetbrains.idea.svn.revert.RevertClient; + +/** + * @author Konstantin Kolosovsky. + */ +public abstract class ClientFactory { + + @NotNull + protected SvnVcs myVcs; + + protected AddClient addClient; + protected AnnotateClient annotateClient; + protected ContentClient contentClient; + protected HistoryClient historyClient; + protected RevertClient revertClient; + protected DeleteClient deleteClient; + protected SvnStatusClientI statusClient; + protected CopyMoveClient copyMoveClient; + protected ConflictClient conflictClient; + protected PropertyClient propertyClient; + + protected ClientFactory(@NotNull SvnVcs vcs) { + myVcs = vcs; + setup(); + } + + protected abstract void setup(); + + @NotNull + public AddClient createAddClient() { + return prepare(addClient); + } + + @NotNull + public AnnotateClient createAnnotateClient() { + return prepare(annotateClient); + } + + @NotNull + public ContentClient createContentClient() { + return prepare(contentClient); + } + + @NotNull + public HistoryClient createHistoryClient() { + return prepare(historyClient); + } + + @NotNull + public RevertClient createRevertClient() { + return prepare(revertClient); + } + + @NotNull + public SvnStatusClientI createStatusClient() { + // TODO: Update this in same like other clients - move to corresponding package, rename clients + return statusClient; + } + + @NotNull + public DeleteClient createDeleteClient() { + return prepare(deleteClient); + } + + @NotNull + public CopyMoveClient createCopyMoveClient() { + return prepare(copyMoveClient); + } + + @NotNull + public ConflictClient createConflictClient() { + return prepare(conflictClient); + } + + @NotNull + public PropertyClient createPropertyClient() { + return prepare(propertyClient); + } + + @NotNull + protected <T extends SvnClient> T prepare(@NotNull T client) { + client.setVcs(myVcs); + + return client; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/CmdClientFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/CmdClientFactory.java new file mode 100644 index 000000000000..ca4088b6a733 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/CmdClientFactory.java @@ -0,0 +1,38 @@ +package org.jetbrains.idea.svn.api; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.add.CmdAddClient; +import org.jetbrains.idea.svn.annotate.CmdAnnotateClient; +import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient; +import org.jetbrains.idea.svn.conflict.CmdConflictClient; +import org.jetbrains.idea.svn.content.CmdContentClient; +import org.jetbrains.idea.svn.copy.CmdCopyMoveClient; +import org.jetbrains.idea.svn.delete.CmdDeleteClient; +import org.jetbrains.idea.svn.history.CmdHistoryClient; +import org.jetbrains.idea.svn.properties.CmdPropertyClient; +import org.jetbrains.idea.svn.revert.CmdRevertClient; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdClientFactory extends ClientFactory { + + public CmdClientFactory(@NotNull SvnVcs vcs) { + super(vcs); + } + + @Override + protected void setup() { + addClient = new CmdAddClient(); + annotateClient = new CmdAnnotateClient(); + contentClient = new CmdContentClient(); + historyClient = new CmdHistoryClient(); + revertClient = new CmdRevertClient(); + deleteClient = new CmdDeleteClient(); + copyMoveClient = new CmdCopyMoveClient(); + conflictClient = new CmdConflictClient(); + propertyClient = new CmdPropertyClient(); + statusClient = new SvnCommandLineStatusClient(myVcs.getProject()); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/FileStatusResultParser.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/FileStatusResultParser.java new file mode 100644 index 000000000000..fc202c265a6a --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/FileStatusResultParser.java @@ -0,0 +1,68 @@ +package org.jetbrains.idea.svn.api; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.containers.Convertor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNEvent; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Konstantin Kolosovsky. + */ +public class FileStatusResultParser { + + private static final double DEFAULT_PROGRESS = 0.0; + + @NotNull + private Pattern myLinePattern; + + @Nullable + private ISVNEventHandler handler; + + @NotNull + private Convertor<Matcher, SVNEvent> myConvertor; + + public FileStatusResultParser(@NotNull Pattern linePattern, + @Nullable ISVNEventHandler handler, + @NotNull Convertor<Matcher, SVNEvent> convertor) { + myLinePattern = linePattern; + this.handler = handler; + myConvertor = convertor; + } + + public void parse(@NotNull String output) throws VcsException { + if (StringUtil.isEmpty(output)) { + return; + } + + for (String line : StringUtil.splitByLines(output)) { + onLine(line); + } + } + + public void onLine(@NotNull String line) throws VcsException { + Matcher matcher = myLinePattern.matcher(line); + if (matcher.matches()) { + process(matcher); + } + else { + throw new VcsException("unknown state on line " + line); + } + } + + public void process(@NotNull Matcher matcher) throws VcsException { + if (handler != null) { + try { + handler.handleEvent(myConvertor.convert(matcher), DEFAULT_PROGRESS); + } catch (SVNException e) { + throw new VcsException(e); + } + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnClient.java new file mode 100644 index 000000000000..90124f1cccb6 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnClient.java @@ -0,0 +1,15 @@ +package org.jetbrains.idea.svn.api; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnVcs; + +/** + * @author Konstantin Kolosovsky. + */ +public interface SvnClient { + + @NotNull + SvnVcs getVcs(); + + void setVcs(@NotNull SvnVcs vcs); +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnKitClientFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnKitClientFactory.java new file mode 100644 index 000000000000..7e1951893969 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnKitClientFactory.java @@ -0,0 +1,38 @@ +package org.jetbrains.idea.svn.api; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.add.SvnKitAddClient; +import org.jetbrains.idea.svn.annotate.SvnKitAnnotateClient; +import org.jetbrains.idea.svn.conflict.SvnKitConflictClient; +import org.jetbrains.idea.svn.content.SvnKitContentClient; +import org.jetbrains.idea.svn.copy.SvnKitCopyMoveClient; +import org.jetbrains.idea.svn.delete.SvnKitDeleteClient; +import org.jetbrains.idea.svn.history.SvnKitHistoryClient; +import org.jetbrains.idea.svn.portable.SvnkitSvnStatusClient; +import org.jetbrains.idea.svn.properties.SvnKitPropertyClient; +import org.jetbrains.idea.svn.revert.SvnKitRevertClient; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitClientFactory extends ClientFactory { + + public SvnKitClientFactory(@NotNull SvnVcs vcs) { + super(vcs); + } + + @Override + protected void setup() { + addClient = new SvnKitAddClient(); + annotateClient = new SvnKitAnnotateClient(); + contentClient = new SvnKitContentClient(); + historyClient = new SvnKitHistoryClient(); + revertClient = new SvnKitRevertClient(); + deleteClient = new SvnKitDeleteClient(); + copyMoveClient = new SvnKitCopyMoveClient(); + conflictClient = new SvnKitConflictClient(); + propertyClient = new SvnKitPropertyClient(); + statusClient = new SvnkitSvnStatusClient(myVcs.createStatusClient()); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/DefaultConfigLoader.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/DefaultConfigLoader.java index 021568aac806..2b779fd0de70 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/DefaultConfigLoader.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/DefaultConfigLoader.java @@ -49,7 +49,7 @@ public class DefaultConfigLoader { final SvnVcs vcs = SvnVcs.getInstance(project); File rootFile = new File(vcsRoot.getPath()); - final SVNInfo info = vcs.createWCClient().doInfo(rootFile, SVNRevision.UNDEFINED); + final SVNInfo info = vcs.getInfo(rootFile); if (info == null || info.getURL() == null) { LOG.info("Directory is not a working copy: " + vcsRoot.getPresentableUrl()); return null; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/SvnBranchConfigurationNew.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/SvnBranchConfigurationNew.java index 28e74e7c395e..483bfda7cfd3 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/SvnBranchConfigurationNew.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/SvnBranchConfigurationNew.java @@ -223,9 +223,8 @@ public class SvnBranchConfigurationNew { private BranchRootSearcher(final SvnVcs vcs, final VirtualFile root) throws SVNException { myRoot = root; myBranchesUnder = new HashMap<String, String>(); - final SVNWCClient client = vcs.createWCClient(); - final SVNInfo info = client.doInfo(new File(myRoot.getPath()), SVNRevision.UNDEFINED); - myRootUrl = info.getURL(); + final SVNInfo info = vcs.getInfo(myRoot.getPath()); + myRootUrl = info != null ? info.getURL() : null; } public boolean accept(final String url) throws SVNException { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java index 3bd8256e7b02..5ac6b967777e 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java @@ -16,8 +16,8 @@ package org.jetbrains.idea.svn.checkin; import com.intellij.openapi.progress.ProgressIndicator; -import org.jetbrains.idea.svn.CommitEventHandler; -import org.jetbrains.idea.svn.CommitEventType; +import org.jetbrains.idea.svn.commandLine.CommitEventHandler; +import org.jetbrains.idea.svn.commandLine.CommitEventType; import org.jetbrains.idea.svn.SvnBundle; import java.io.File; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java index 995646f50d45..f765595745b3 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java @@ -28,6 +28,7 @@ import com.intellij.util.net.HttpConfigurable; import com.intellij.util.proxy.CommonProxy; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; +import org.jetbrains.idea.svn.commandLine.AuthenticationCallback; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.auth.*; import org.tmatesoft.svn.core.internal.util.SVNBase64; @@ -77,6 +78,20 @@ public class IdeaSvnkitBasedAuthenticationCallback implements AuthenticationCall return new CredentialsAuthenticator(myVcs).tryAuthenticate(realm, url, file, previousFailed, passwordRequest); } + @Nullable + @Override + public SVNAuthentication requestCredentials(@Nullable SVNURL url, String type) { + SVNAuthentication authentication = + url != null ? myVcs.getSvnConfiguration().getInteractiveManager(myVcs).getProvider().requestClientAuthentication( + type, url, url.toDecodedString(), null, null, true) : null; + + if (authentication == null) { + LOG.warn("Could not get authentication. Type - " + type + ", Url - " + url); + } + + return authentication; + } + @Override public boolean acceptSSLServerCertificate(final File file, final String realm) { final File base = getExistingParent(file); @@ -354,7 +369,9 @@ public class IdeaSvnkitBasedAuthenticationCallback implements AuthenticationCall }, new ThrowableRunnable<SVNException>() { @Override public void run() throws SVNException { + // NOTE: DO NOT replace this call - SSL authentication highly tied to SVNKit myVcs.createWCClient(active).doInfo(myUrl, SVNRevision.UNDEFINED, SVNRevision.HEAD); + //myVcs.getInfo(myUrl, SVNRevision.HEAD, active); } } ); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java index 433e4063e02c..c7d2d4b41bf2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java @@ -48,8 +48,8 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; +import org.jetbrains.idea.svn.commandLine.SvnBindClient; import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient; -import org.tigris.subversion.javahl.ClientException; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.*; @@ -177,7 +177,7 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { if (committables.isEmpty()) { return; } - if (WorkingCopyFormat.ONE_DOT_SEVEN.equals(format) && + if (WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) || WorkingCopyFormat.ONE_DOT_SEVEN.equals(format) && SvnConfiguration.UseAcceleration.commandLine.equals(SvnConfiguration.getInstance(mySvnVcs.getProject()).myUseAcceleration) && (SvnAuthenticationManager.HTTP.equals(url.getProtocol()) || SvnAuthenticationManager.HTTPS.equals(url.getProtocol()))) { doWithCommandLine(committables, comment, exception, feedback); @@ -256,14 +256,25 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { }); final IdeaSvnkitBasedAuthenticationCallback authenticationCallback = new IdeaSvnkitBasedAuthenticationCallback(mySvnVcs); try { - final SvnBindClient client = new SvnBindClient(SvnApplicationSettings.getInstance().getCommandLinePath()); + final SvnBindClient client = new SvnBindClient(SvnApplicationSettings.getInstance().getCommandLinePath(), new Convertor<String[], SVNURL>() { + @Override + public SVNURL convert(String[] o) { + SVNInfo info = o.length > 0 ? mySvnVcs.getInfo(o[0]) : null; + + if (info == null || info.getURL() == null) { + LOG.warn("Could not resolve repository url for commit. Paths - " + Arrays.toString(o)); + } + + return info != null ? info.getURL() : null; + } + }); client.setAuthenticationCallback(authenticationCallback); client.setHandler(new IdeaCommitHandler(ProgressManager.getInstance().getProgressIndicator())); final long revision = client.commit(ArrayUtil.toStringArray(paths), comment, false, false); reportCommittedRevisions(feedback, String.valueOf(revision)); } - catch (ClientException e) { - exception.add(new VcsException(e)); + catch (VcsException e) { + exception.add(e); } finally { authenticationCallback.reset(); } @@ -348,25 +359,19 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { private List<File> getCommitables(List<File> paths) { final Adder adder = new Adder(); - SVNStatusClient statusClient = mySvnVcs.createStatusClient(); for (File path : paths) { File file = path.getAbsoluteFile(); adder.add(file); if (file.getParentFile() != null) { - addParents(statusClient, file.getParentFile(), adder); + addParents(file.getParentFile(), adder); } } return adder.getResult(); } - private static void addParents(SVNStatusClient statusClient, File file, final Adder adder) { - SVNStatus status; - try { - status = statusClient.doStatus(file, false); - } - catch (SVNException e) { - return; - } + private void addParents(File file, final Adder adder) { + SVNStatus status = getStatus(file); + if (status != null && (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_ADDED) || SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_REPLACED))) { @@ -374,11 +379,33 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { adder.add(file); file = file.getParentFile(); if (file != null) { - addParents(statusClient, file, adder); + addParents(file, adder); } } } + private SVNStatus getStatus(File file) { + SVNStatus result = null; + WorkingCopyFormat format = mySvnVcs.getWorkingCopyFormat(file); + + try { + result = WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) ? getStatusCommandLine(file) : getStatusSvnKit(file); + } + catch (SVNException e) { + // do nothing + } + + return result; + } + + private SVNStatus getStatusSvnKit(File file) throws SVNException { + return mySvnVcs.createStatusClient().doStatus(file, false); + } + + private SVNStatus getStatusCommandLine(File file) throws SVNException { + return new SvnCommandLineStatusClient(mySvnVcs.getProject()).doStatus(file, false); + } + private static List<File> collectPaths(final List<Change> changes) { // case sensitive.. ArrayList<File> result = new ArrayList<File>(); @@ -418,15 +445,14 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { public List<VcsException> scheduleMissingFileForDeletion(List<FilePath> filePaths) { List<VcsException> exceptions = new ArrayList<VcsException>(); - final SVNWCClient wcClient = mySvnVcs.createWCClient(); - List<File> files = ChangesUtil.filePathsToFiles(filePaths); + for (File file : files) { try { - wcClient.doDelete(file, true, false); + mySvnVcs.getFactory(file).createDeleteClient().delete(file, true); } - catch (SVNException e) { - exceptions.add(new VcsException(e)); + catch (VcsException e) { + exceptions.add(e); } } @@ -434,30 +460,22 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { } public List<VcsException> scheduleUnversionedFilesForAddition(List<VirtualFile> files) { - final List<VcsException> result = new ArrayList<VcsException>(); - final SVNWCClient wcClient = mySvnVcs.createWCClient(); - - final List<SVNException> exceptionList = scheduleUnversionedFilesForAddition(wcClient, files); - for (SVNException svnException : exceptionList) { - result.add(new VcsException(svnException)); - } - return result; + return scheduleUnversionedFilesForAddition(mySvnVcs, files); } - public static List<SVNException> scheduleUnversionedFilesForAddition(SVNWCClient wcClient, List<VirtualFile> files) { - return scheduleUnversionedFilesForAddition(wcClient, files, false); + public static List<VcsException> scheduleUnversionedFilesForAddition(@NotNull SvnVcs vcs, List<VirtualFile> files) { + return scheduleUnversionedFilesForAddition(vcs, files, false); } - public static List<SVNException> scheduleUnversionedFilesForAddition(SVNWCClient wcClient, List<VirtualFile> files, final boolean recursive) { - List<SVNException> exceptions = new ArrayList<SVNException>(); - + public static List<VcsException> scheduleUnversionedFilesForAddition(@NotNull SvnVcs vcs, List<VirtualFile> files, final boolean recursive) { Collections.sort(files, FilePathComparator.getInstance()); - wcClient.setEventHandler(new ISVNEventHandler() { + ISVNEventHandler eventHandler = new ISVNEventHandler() { @Override public void handleEvent(SVNEvent event, double progress) throws SVNException { final ProgressManager pm = ProgressManager.getInstance(); final ProgressIndicator pi = pm.getProgressIndicator(); + // TODO: pi is null here when invoking "Add" action if (pi != null && event.getFile() != null) { File file = event.getFile(); pi.setText(SvnBundle.message("progress.text2.adding", file.getName() + " (" + file.getParent() + ")")); @@ -472,12 +490,18 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { if (pi.isCanceled()) throw new SVNCancelException(); } } - }); + }; + + List<VcsException> exceptions = new ArrayList<VcsException>(); + for (VirtualFile file : files) { try { - wcClient.doAdd(new File(FileUtil.toSystemDependentName(file.getPath())), true, false, true, recursive); + File convertedFile = new File(FileUtil.toSystemDependentName(file.getPath())); + SVNDepth depth = recursive ? SVNDepth.INFINITY : SVNDepth.EMPTY; + + vcs.getFactory(convertedFile).createAddClient().add(convertedFile, depth, true, false, true, eventHandler); } - catch (SVNException e) { + catch (VcsException e) { exceptions.add(e); } } diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/AuthenticationCallback.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AuthenticationCallback.java index bdc9c2deb03c..1944749f3608 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/AuthenticationCallback.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AuthenticationCallback.java @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn; +package org.jetbrains.idea.svn.commandLine; import org.jetbrains.annotations.Nullable; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.auth.SVNAuthentication; import java.io.File; import java.io.IOException; @@ -51,6 +53,17 @@ public interface AuthenticationCallback { boolean authenticateFor(@Nullable String realm, File base, boolean previousFailed, boolean passwordRequest); /** + * Provides authentication information to access given url by authentication protocol identified by type. + * For instance, username/password for http/svn protocols. SSL client certificate for two way SSL protocol. + * + * @param url url to item in repository + * @param type authentication protocol type with svn specific values, like "svn.simple" for http. + * @return + */ + @Nullable + SVNAuthentication requestCredentials(@Nullable SVNURL url, String type); + + /** * @return config directory if TMP was created */ @Nullable diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java new file mode 100644 index 000000000000..737d8a85b60b --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java @@ -0,0 +1,218 @@ +package org.jetbrains.idea.svn.commandLine; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnApplicationSettings; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.api.FileStatusResultParser; +import org.jetbrains.idea.svn.checkin.IdeaSvnkitBasedAuthenticationCallback; +import org.tmatesoft.svn.core.*; +import org.tmatesoft.svn.core.wc.SVNDiffOptions; +import org.tmatesoft.svn.core.wc.SVNInfo; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNStatusType; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CommandUtil { + public static SvnLineCommand runSimple(@NotNull SvnCommandName name, + @NotNull SvnVcs vcs, + @Nullable File base, + @Nullable SVNURL url, + List<String> parameters) + throws SVNException { + String exe = resolveExePath(); + base = resolveBaseDirectory(base, exe); + url = resolveRepositoryUrl(vcs, url); + + try { + return SvnLineCommand + .runWithAuthenticationAttempt(exe, base, url, name, new SvnCommitRunner.CommandListener(null), + new IdeaSvnkitBasedAuthenticationCallback(vcs), ArrayUtil.toStringArray(parameters)); + } + catch (SvnBindException e) { + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); + } + } + + @Nullable + private static SVNURL resolveRepositoryUrl(@NotNull SvnVcs vcs, @Nullable SVNURL url) { + if (url == null) { + // TODO: or take it from RootUrlInfo + SVNInfo info = vcs.getInfo(vcs.getProject().getBaseDir()); + + url = info != null ? info.getURL() : null; + } + return url; + } + + @NotNull + private static File resolveBaseDirectory(@Nullable File base, @NotNull String defaultBase) { + return base == null ? new File(defaultBase) : base; + } + + @NotNull + private static String resolveExePath() { + return SvnApplicationSettings.getInstance().getCommandLinePath(); + } + + public static SvnLineCommand runSimple(@NotNull SvnSimpleCommand command, @NotNull SvnVcs vcs, @Nullable File base, @Nullable SVNURL url) + throws SVNException { + // empty command name passed, as command name is already in command.getParameters() + return runSimple(SvnCommandName.empty, vcs, base, url, new ArrayList<String>(Arrays.asList(command.getParameters()))); + } + + /** + * Puts given value to parameters if condition is satisfied + * + * @param parameters + * @param condition + * @param value + */ + public static void put(@NotNull List<String> parameters, boolean condition, @NotNull String value) { + if (condition) { + parameters.add(value); + } + } + + public static void put(@NotNull List<String> parameters, @NotNull File path) { + parameters.add(path.getAbsolutePath()); + } + + public static void put(@NotNull List<String> parameters, @NotNull File path, @Nullable SVNRevision pegRevision) { + put(parameters, path.getAbsolutePath(), pegRevision); + } + + public static void put(@NotNull List<String> parameters, @NotNull String path, @Nullable SVNRevision pegRevision) { + StringBuilder builder = new StringBuilder(path); + + if (pegRevision != null && !SVNRevision.UNDEFINED.equals(pegRevision) && !SVNRevision.WORKING.equals(pegRevision) && + pegRevision.getNumber() > 0) { + builder.append("@"); + builder.append(pegRevision); + } + + parameters.add(builder.toString()); + } + + public static void put(@NotNull List<String> parameters, @NotNull File... paths) { + for (File path : paths) { + put(parameters, path); + } + } + + public static void put(@NotNull List<String> parameters, @Nullable SVNDepth depth) { + if (depth != null && !SVNDepth.UNKNOWN.equals(depth)) { + parameters.add("--depth"); + parameters.add(depth.getName()); + } + } + + public static void put(@NotNull List<String> parameters, @Nullable SVNRevision revision) { + if (revision != null && !SVNRevision.UNDEFINED.equals(revision) && !SVNRevision.WORKING.equals(revision) && revision.getNumber() >= 0) { + parameters.add("--revision"); + parameters.add(revision.toString()); + } + } + + public static void put(@NotNull List<String> parameters, @Nullable SVNDiffOptions diffOptions) { + if (diffOptions != null) { + StringBuilder builder = new StringBuilder(); + + if (diffOptions.isIgnoreAllWhitespace()) { + builder.append(" --ignore-space-change"); + } + if (diffOptions.isIgnoreAmountOfWhitespace()) { + builder.append(" --ignore-all-space"); + } + if (diffOptions.isIgnoreEOLStyle()) { + builder.append(" --ignore-eol-style"); + } + + String value = builder.toString().trim(); + + if (!StringUtil.isEmpty(value)) { + parameters.add("--extensions"); + parameters.add(value); + } + } + } + + /** + * Utility method for running commands changing certain file status information. + * // TODO: Should be replaced with non-static analogue. + * + * @param vcs + * @param name + * @param parameters + * @param parser + * @throws VcsException + */ + public static SvnCommand execute(@NotNull SvnVcs vcs, + @NotNull SvnCommandName name, + @NotNull List<String> parameters, + @Nullable FileStatusResultParser parser) + throws VcsException { + String exe = resolveExePath(); + File base = resolveBaseDirectory(null, exe); + SVNURL url = resolveRepositoryUrl(vcs, null); + + SvnLineCommand command = SvnLineCommand.runWithAuthenticationAttempt( + exe, base, url, name, new SvnCommitRunner.CommandListener(null), + new IdeaSvnkitBasedAuthenticationCallback(vcs), + ArrayUtil.toStringArray(parameters)); + + if (parser != null) { + parser.parse(command.getOutput()); + } + + return command; + } + + /** + * Gets svn status represented by single character. + * + * @param type + * @return + */ + public static char getStatusChar(@Nullable String type) { + return !StringUtil.isEmpty(type) ? type.charAt(0) : ' '; + } + + @NotNull + public static SVNStatusType getStatusType(@Nullable String type) { + return getStatusType(getStatusChar(type)); + } + + @NotNull + public static SVNStatusType getStatusType(char first) { + final SVNStatusType contentsStatus; + if ('A' == first) { + contentsStatus = SVNStatusType.STATUS_ADDED; + } else if ('D' == first) { + contentsStatus = SVNStatusType.STATUS_DELETED; + } else if ('U' == first) { + contentsStatus = SVNStatusType.CHANGED; + } else if ('C' == first) { + contentsStatus = SVNStatusType.CONFLICTED; + } else if ('G' == first) { + contentsStatus = SVNStatusType.MERGED; + } else if ('R' == first) { + contentsStatus = SVNStatusType.STATUS_REPLACED; + } else if ('E' == first) { + contentsStatus = SVNStatusType.STATUS_OBSTRUCTED; + } else { + contentsStatus = SVNStatusType.STATUS_NORMAL; + } + return contentsStatus; + } +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventHandler.java index 7f3bcf3bf4a5..d74884073863 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventHandler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn; +package org.jetbrains.idea.svn.commandLine; import java.io.File; diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventType.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventType.java index 46609e40532c..87dc212d0341 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventType.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventType.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn; +package org.jetbrains.idea.svn.commandLine; /** * Created with IntelliJ IDEA. diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java index ccbc5c37db61..ccbc5c37db61 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindClient.java new file mode 100644 index 000000000000..8f14cb269179 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindClient.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.svn.commandLine; + +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.containers.Convertor; +import org.tmatesoft.svn.core.SVNURL; + +import java.util.Map; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/5/13 + * Time: 3:08 PM + */ +public class SvnBindClient { + private final String myExecutablePath; + private CommitEventHandler myHandler; + private AuthenticationCallback myAuthenticationCallback; + private Convertor<String[], SVNURL> myUrlProvider; + + public SvnBindClient(String path, Convertor<String[], SVNURL> urlProvider) { + myExecutablePath = path; + myUrlProvider = urlProvider; + } + + public long commit(String[] path, String message, boolean recurse, boolean noUnlock) throws VcsException { + return commit(path, message, recurse? 3 : 0, noUnlock, false, null, null); + } + + public long commit(String[] path, + String message, + int depth, + boolean noUnlock, + boolean keepChangelist, + String[] changelists, + Map revpropTable) throws VcsException { + final long commit = new SvnCommitRunner(myExecutablePath, myHandler, myAuthenticationCallback). + commit(path, message, depth, noUnlock, keepChangelist, changelists, revpropTable, myUrlProvider); + if (commit < 0) { + throw new VcsException("Wrong committed revision number: " + commit); + } + return commit; + } + + public void setHandler(CommitEventHandler handler) { + myHandler = handler; + } + + public void setAuthenticationCallback(AuthenticationCallback authenticationCallback) { + myAuthenticationCallback = authenticationCallback; + } +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/config/SvnBindException.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java index 8957843e911c..524b928eae92 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/config/SvnBindException.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java @@ -13,12 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.config; +package org.jetbrains.idea.svn.commandLine; import com.intellij.openapi.vcs.VcsException; -import java.util.Collection; - /** * Created with IntelliJ IDEA. * User: Irina.Chernushina @@ -32,23 +30,7 @@ public class SvnBindException extends VcsException { super(message); } - public SvnBindException(Throwable throwable, boolean isWarning) { - super(throwable, isWarning); - } - public SvnBindException(Throwable throwable) { super(throwable); } - - public SvnBindException(String message, Throwable cause) { - super(message, cause); - } - - public SvnBindException(String message, boolean isWarning) { - super(message, isWarning); - } - - public SvnBindException(Collection<String> messages) { - super(messages); - } } diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindUtil.java index 1fec6b3bf865..7d663bae65b5 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindUtil.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindUtil.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn; +package org.jetbrains.idea.svn.commandLine; import java.io.File; import java.text.DateFormat; @@ -31,16 +31,7 @@ import java.util.Locale; * Time: 4:56 PM */ public class SvnBindUtil { - /** - * SVN_ASP_DOT_NET_HACK allows use of an alternate name for Subversion working copy - * administrative directories on Windows (which were formerly always - * named ".svn"), by setting the SVN_ASP_DOT_NET_HACK environment variable. - * When the variable is set (to any value), the administrative directory - * will be "_svn" instead of ".svn". - * - * http://svn.apache.org/repos/asf/subversion/trunk/notes/asp-dot-net-hack.txt - */ - public static final String ADM_NAME = System.getenv("SVN_ASP_DOT_NET_HACK") != null ? "_svn" : ".svn"; + private final static List<DateFormat> ourFormats = new ArrayList<DateFormat>(); static { @@ -88,17 +79,4 @@ public class SvnBindUtil { } return null; } - - public static File getWcRoot(File base) { - File current = base; - while (current != null) { - if (getWcDbUnder(current).exists()) return current; - current = current.getParentFile(); - } - return null; - } - - public static File getWcDbUnder(final File file) { - return new File(file, ADM_NAME + File.separator + "wc.db"); - } } diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java index d6df6bae947e..9e355e56f4d6 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java @@ -16,6 +16,7 @@ package org.jetbrains.idea.svn.commandLine; import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.execution.process.CapturingProcessAdapter; import com.intellij.execution.process.OSProcessHandler; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessListener; @@ -38,6 +39,7 @@ import java.util.List; */ public abstract class SvnCommand { static final Logger LOG = Logger.getInstance(SvnCommand.class.getName()); + private final File myConfigDir; private boolean myIsDestroyed; private int myExitCode; @@ -45,13 +47,13 @@ public abstract class SvnCommand { private final File myWorkingDirectory; private Process myProcess; private OSProcessHandler myHandler; + // TODO: Try to implement commands in a way that they manually indicate if they need full output - to prevent situations + // TODO: when large amount of data needs to be stored instead of just sequential processing. + private CapturingProcessAdapter outputAdapter; private final Object myLock; private final EventDispatcher<ProcessEventListener> myListeners = EventDispatcher.create(ProcessEventListener.class); - - // todo check version - /*c:\Program Files (x86)\CollabNet\Subversion Client17>svn --version --quiet - 1.7.2*/ + private final SvnCommandName myCommandName; public SvnCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath) { this(workingDirectory, commandName, exePath, null); @@ -59,15 +61,34 @@ public abstract class SvnCommand { public SvnCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath, @Nullable File configDir) { + myCommandName = commandName; myLock = new Object(); myCommandLine = new GeneralCommandLine(); myWorkingDirectory = workingDirectory; myCommandLine.setExePath(exePath); myCommandLine.setWorkDirectory(workingDirectory); + myConfigDir = configDir; if (configDir != null) { myCommandLine.addParameters("--config-dir", configDir.getPath()); } - myCommandLine.addParameter(commandName.getName()); + if (!SvnCommandName.empty.equals(commandName)) { + myCommandLine.addParameter(commandName.getName()); + } + } + + public String[] getParameters() { + synchronized (myLock) { + return myCommandLine.getParametersList().getArray(); + } + } + + /** + * Indicates if process was destroyed "manually" by command execution logic. + * + * @return + */ + public boolean isManuallyDestroyed() { + return myIsDestroyed; } public void start() { @@ -112,10 +133,16 @@ public abstract class SvnCommand { } }; + outputAdapter = new CapturingProcessAdapter(); + myHandler.addProcessListener(outputAdapter); myHandler.addProcessListener(processListener); myHandler.startNotify(); } + public String getOutput() { + return outputAdapter.getOutput().getStdout(); + } + /** * Wait for process termination * @param timeout @@ -186,6 +213,18 @@ public abstract class SvnCommand { } } + public String getCommandText() { + synchronized (myLock) { + return myCommandLine.getCommandLineString(); + } + } + + public String getExePath() { + synchronized (myLock) { + return myCommandLine.getExePath(); + } + } + /** * check that process is not started yet * @@ -226,4 +265,8 @@ public abstract class SvnCommand { protected File getWorkingDirectory() { return myWorkingDirectory; } + + public SvnCommandName getCommandName() { + return myCommandName; + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java index 0d2f4d00bd39..47b6b5234396 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java @@ -15,12 +15,13 @@ */ package org.jetbrains.idea.svn.commandLine; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.util.Consumer; -import org.jetbrains.idea.svn.SvnBindUtil; +import org.jetbrains.idea.svn.SvnApplicationSettings; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.portable.SvnExceptionWrapper; import org.jetbrains.idea.svn.portable.SvnkitSvnWcClient; @@ -36,7 +37,9 @@ import javax.xml.parsers.SAXParserFactory; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; /** * Created with IntelliJ IDEA. @@ -45,9 +48,12 @@ import java.util.Collection; * Time: 12:59 PM */ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient { + + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.commandLine.SvnCommandLineInfoClient"); private final Project myProject; public SvnCommandLineInfoClient(final Project project) { + // TODO: Remove svn kit client instantiation super(SvnVcs.getInstance(project).createWCClient()); myProject = project; } @@ -74,22 +80,56 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient { base = SvnBindUtil.correctUpToExistingParent(base); if (base == null) { // very unrealistic - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), new RuntimeException("Can not find existing parent file")); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Can not find existing parent file")); } + issueCommand(path.getAbsolutePath(), pegRevision, revision, depth, changeLists, handler, base); + } + + private void issueCommand(String path, SVNRevision pegRevision, + SVNRevision revision, + SVNDepth depth, + Collection changeLists, + final ISVNInfoHandler handler, File base) throws SVNException { final SvnSimpleCommand command = SvnCommandFactory.createSimpleCommand(myProject, base, SvnCommandName.info); + List<String> parameters = new ArrayList<String>(); + + fillParameters(path, pegRevision, revision, depth, parameters); + command.addParameters(parameters); + SvnCommandLineStatusClient.changelistsToCommand(changeLists, command); - if (depth != null) { - command.addParameters("--depth", depth.getName()); + parseResult(handler, base, execute(command)); + } + + private String execute(SvnSimpleCommand command) throws SVNException { + try { + return command.run(); } - if (revision != null && ! SVNRevision.UNDEFINED.equals(revision) && ! SVNRevision.WORKING.equals(revision)) { - command.addParameters("-r", revision.toString()); + catch (VcsException e) { + final String text = e.getMessage(); + final boolean notEmpty = !StringUtil.isEmptyOrSpaces(text); + if (notEmpty && text.contains("W155010")) { + // just null + return null; + } + // not a working copy exception + // "E155007: '' is not a working copy" + if (notEmpty && text.contains("is not a working copy")) { + if (StringUtil.isNotEmpty(command.getOutput())) { + // workaround: as in subversion 1.8 "svn info" on a working copy root outputs such error for parent folder, + // if there are files with conflicts. + // but the requested info is still in the output except root closing tag + return command.getOutput() + "</info>"; + } else { + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.WC_NOT_WORKING_COPY, e), e); + } + } + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } - command.addParameters("--xml"); - SvnCommandLineStatusClient.changelistsToCommand(changeLists, command); - if (pegRevision != null && ! SVNRevision.UNDEFINED.equals(pegRevision) && ! SVNRevision.WORKING.equals(pegRevision)) { - command.addParameters(path.getPath() + "@" + pegRevision.toString()); - } else { - command.addParameters(path.getPath()); + } + + private void parseResult(final ISVNInfoHandler handler, File base, String result) throws SVNException { + if (StringUtil.isEmpty(result)) { + return; } final SvnInfoHandler[] infoHandler = new SvnInfoHandler[1]; @@ -106,38 +146,34 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient { }); try { - final String result = command.run(); SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); - parser.parse(new ByteArrayInputStream(result.getBytes(CharsetToolkit.UTF8_CHARSET)), infoHandler[0]); + parser.parse(new ByteArrayInputStream(result.getBytes(CharsetToolkit.UTF8_CHARSET)), infoHandler[0]); } catch (SvnExceptionWrapper e) { + LOG.info("info output " + result); throw (SVNException) e.getCause(); } catch (IOException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + LOG.info("info output " + result); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } catch (ParserConfigurationException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + LOG.info("info output " + result); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } catch (SAXException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); - } - catch (VcsException e) { - final String text = e.getMessage(); - final boolean notEmpty = !StringUtil.isEmptyOrSpaces(text); - if (notEmpty && text.contains("W155010")) { - // just null - return; - } - // not a working copy exception - // "E155007: '' is not a working copy" - if (notEmpty && text.contains("is not a working copy")) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.WC_NOT_WORKING_COPY), e); - } - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + LOG.info("info output " + result); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } } + private void fillParameters(String path, SVNRevision pegRevision, SVNRevision revision, SVNDepth depth, List<String> parameters) { + CommandUtil.put(parameters, depth); + CommandUtil.put(parameters, revision); + CommandUtil.put(parameters, path, pegRevision); + parameters.add("--xml"); + } + @Override public void doInfo(SVNURL url, SVNRevision pegRevision, SVNRevision revision, boolean recursive, ISVNInfoHandler handler) throws SVNException { @@ -147,7 +183,14 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient { @Override public void doInfo(SVNURL url, SVNRevision pegRevision, SVNRevision revision, SVNDepth depth, ISVNInfoHandler handler) throws SVNException { - throw new UnsupportedOperationException(); + String path = url.toDecodedString(); + List<String> parameters = new ArrayList<String>(); + + fillParameters(path, pegRevision, revision, depth, parameters); + File base = new File(SvnApplicationSettings.getInstance().getCommandLinePath()); + String result = CommandUtil.runSimple(SvnCommandName.info, SvnVcs.getInstance(myProject), base, url, parameters).getOutput(); + + parseResult(handler, base, result); } @Override diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java index a1a31536c8a9..c1429be699af 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java @@ -15,14 +15,16 @@ */ package org.jetbrains.idea.svn.commandLine; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Getter; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.util.containers.Convertor; -import org.jetbrains.idea.svn.SvnBindUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnUtil; +import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.portable.PortableStatus; import org.jetbrains.idea.svn.portable.SvnExceptionWrapper; import org.jetbrains.idea.svn.portable.SvnStatusClientI; @@ -48,6 +50,8 @@ import java.util.Map; * Time: 5:21 PM */ public class SvnCommandLineStatusClient implements SvnStatusClientI { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient"); + private final Project myProject; private final SvnCommandLineInfoClient myInfoClient; @@ -102,47 +106,84 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI { final SVNInfo infoBase = myInfoClient.doInfo(base, revision); final SvnSimpleCommand command = SvnCommandFactory.createSimpleCommand(myProject, base, SvnCommandName.st); - putParameters(depth, remote, reportAll, includeIgnored, changeLists, command); + putParameters(path, depth, remote, reportAll, includeIgnored, changeLists, command); + + parseResult(path, revision, handler, base, infoBase, command, execute(command, base)); + return 0; + } + + private String execute(SvnSimpleCommand command, File base) throws SVNException { + String result = CommandUtil.runSimple(command, SvnVcs.getInstance(myProject), base, null).getOutput(); - final SvnStatusHandler[] svnHandl = new SvnStatusHandler[1]; - svnHandl[0] = createStatusHandler(revision, handler, base, infoBase, svnHandl); + if (StringUtil.isEmptyOrSpaces(result)) { + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.FS_GENERAL, "Status request returned nothing for command: " + + command.myCommandLine.getCommandLineString())); + } + + return result; + } + + private void parseResult(final File path, + SVNRevision revision, + ISVNStatusHandler handler, + File base, + SVNInfo infoBase, + SvnSimpleCommand command, String result) throws SVNException { + + if (StringUtil.isEmpty(result)) { + return; + } try { - final String result = command.run(); - if (StringUtil.isEmptyOrSpaces(result)) { - throw new VcsException("Status request returned nothing for command: " + command.myCommandLine.getCommandLineString()); - } + final SvnStatusHandler[] svnHandl = new SvnStatusHandler[1]; + svnHandl[0] = createStatusHandler(revision, handler, base, infoBase, svnHandl); SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); parser.parse(new ByteArrayInputStream(result.getBytes(CharsetToolkit.UTF8_CHARSET)), svnHandl[0]); - if (! svnHandl[0].isAnythingReported()) { - if (! SvnUtil.isSvnVersioned(myProject, path)) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY)); + if (!svnHandl[0].isAnythingReported()) { + if (!SvnUtil.isSvnVersioned(myProject, path)) { + throw new SVNException( + SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY, "Command - " + command.getCommandText() + ". Result - " + result)); + } else { + // return status indicating "NORMAL" state + // typical output would be like + // <status> + // <target path="1.txt"></target> + // </status> + // so it does not contain any <entry> element and current parsing logic returns null + + PortableStatus status = new PortableStatus(); + status.setPath(path.getAbsolutePath()); + status.setContentsStatus(SVNStatusType.STATUS_NORMAL); + status.setInfoGetter(new Getter<SVNInfo>() { + @Override + public SVNInfo get() { + return createInfoGetter(null).convert(path); + } + }); + handler.handleStatus(status); } } } catch (SvnExceptionWrapper e) { throw (SVNException) e.getCause(); } catch (IOException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } catch (ParserConfigurationException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } catch (SAXException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); - } - catch (VcsException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } - return 0; } - private void putParameters(SVNDepth depth, + private void putParameters(@NotNull File path, SVNDepth depth, boolean remote, boolean reportAll, boolean includeIgnored, Collection changeLists, SvnSimpleCommand command) { + command.addParameters(path.getAbsolutePath()); if (depth != null) { command.addParameters("--depth", depth.getName()); } @@ -171,7 +212,11 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI { final SVNInfo infoBase, final SvnStatusHandler[] svnHandl) { final SvnStatusHandler.ExternalDataCallback callback = createStatusCallback(handler, base, infoBase, svnHandl); - return new SvnStatusHandler(callback, base, new Convertor<File, SVNInfo>() { + return new SvnStatusHandler(callback, base, createInfoGetter(revision)); + } + + private Convertor<File, SVNInfo> createInfoGetter(final SVNRevision revision) { + return new Convertor<File, SVNInfo>() { @Override public SVNInfo convert(File o) { try { @@ -181,7 +226,7 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI { throw new SvnExceptionWrapper(e); } } - }); + }; } public static SvnStatusHandler.ExternalDataCallback createStatusCallback(final ISVNStatusHandler handler, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java index c0e9c8e79fa2..e0834c2f116f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java @@ -24,7 +24,6 @@ import com.intellij.util.ArrayUtil; import org.jetbrains.idea.svn.SvnApplicationSettings; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.checkin.IdeaSvnkitBasedAuthenticationCallback; -import org.jetbrains.idea.svn.config.SvnBindException; import org.jetbrains.idea.svn.portable.SvnSvnkitUpdateClient; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.*; @@ -49,9 +48,9 @@ public class SvnCommandLineUpdateClient extends SvnSvnkitUpdateClient { private final VirtualFile myCommonAncestor; private boolean myIgnoreExternals; - public SvnCommandLineUpdateClient(final Project project, VirtualFile commonAncestor) { - super(SvnVcs.getInstance(project).createUpdateClient()); - myProject = project; + public SvnCommandLineUpdateClient(final SvnVcs vcs, VirtualFile commonAncestor) { + super(vcs.createUpdateClient()); + myProject = vcs.getProject(); myCommonAncestor = commonAncestor; } @@ -88,35 +87,7 @@ public class SvnCommandLineUpdateClient extends SvnSvnkitUpdateClient { File base = myCommonAncestor == null ? paths[0] : new File(myCommonAncestor.getPath()); base = base.isDirectory() ? base : base.getParentFile(); - final List<String> parameters = new ArrayList<String>(); - if (revision != null && ! SVNRevision.UNDEFINED.equals(revision) && ! SVNRevision.WORKING.equals(revision)) { - parameters.add("-r"); - parameters.add(revision.toString()); - } - // unknown depth is not used any more for 1.7 -> why? - if (depth != null && ! SVNDepth.UNKNOWN.equals(depth)) { - parameters.add("--depth"); - parameters.add(depth.toString()); - } - if (allowUnversionedObstructions) { - parameters.add("--force"); - } - if (depthIsSticky && depth != null) {// !!! not sure, but not used - parameters.add("--set-depth"); - parameters.add(depth.toString()); - } - if (makeParents) { - parameters.add("--parents"); - } - if (myIgnoreExternals) { - parameters.add("--ignore-externals"); - } - parameters.add("--accept"); - parameters.add("postpone"); - - for (File path : paths) { - parameters.add(path.getPath()); - } + final List<String> parameters = prepareParameters(paths, revision, depth, allowUnversionedObstructions, depthIsSticky, makeParents); final AtomicReference<SVNException> excRef = new AtomicReference<SVNException>(); final ISVNEventHandler handler = getEventHandler(); @@ -166,7 +137,7 @@ public class SvnCommandLineUpdateClient extends SvnSvnkitUpdateClient { } }; SvnLineCommand.runWithAuthenticationAttempt(SvnApplicationSettings.getInstance().getCommandLinePath(), - base, SvnCommandName.up, listener, + base, info.getURL(), SvnCommandName.up, listener, new IdeaSvnkitBasedAuthenticationCallback(SvnVcs.getInstance(myProject)), ArrayUtil.toStringArray(parameters)); } @@ -180,6 +151,29 @@ public class SvnCommandLineUpdateClient extends SvnSvnkitUpdateClient { return updatedToRevision.get(); } + private List<String> prepareParameters(File[] paths, + SVNRevision revision, + SVNDepth depth, + boolean allowUnversionedObstructions, + boolean depthIsSticky, boolean makeParents) { + List<String> parameters = new ArrayList<String>(); + + CommandUtil.put(parameters, revision); + CommandUtil.put(parameters, depth); + CommandUtil.put(parameters, allowUnversionedObstructions, "--force"); + if (depthIsSticky && depth != null) {// !!! not sure, but not used + parameters.add("--set-depth"); + parameters.add(depth.toString()); + } + CommandUtil.put(parameters, makeParents, "--parents"); + CommandUtil.put(parameters, myIgnoreExternals, "--ignore-externals"); + parameters.add("--accept"); + parameters.add("postpone"); + CommandUtil.put(parameters, paths); + + return parameters; + } + private void checkForException(final StringBuffer sbError) throws SVNException { if (sbError.length() == 0) return; diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java index 58c2ff2ab308..c19ea283bbd1 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java @@ -22,13 +22,25 @@ package org.jetbrains.idea.svn.commandLine; * Time: 1:49 PM */ public enum SvnCommandName { + // TODO: temporary command for "more smooth" converting between simple commands and line commands + empty("", false), version("--version", false), info("info", false), st("st", false), up("up", true), ci("commit", true), - cleanup("cleanup", true); - + cleanup("cleanup", true), + cat("cat", false), + add("add", true), + log("log", false), + revert("revert", true), + delete("delete", true), + copy("copy", true), + move("move", true), + resolve("resolve", true), + propget("propget", false), + blame("blame", false); + private final String myName; private final boolean myWriteable; diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnCommitRunner.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommitRunner.java index 525d675303fc..83f6ec156cf1 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnCommitRunner.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommitRunner.java @@ -13,22 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn; +package org.jetbrains.idea.svn.commandLine; import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.Convertor; import org.apache.subversion.javahl.types.Revision; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.commandLine.LineCommandListener; -import org.jetbrains.idea.svn.commandLine.SvnCommandName; -import org.jetbrains.idea.svn.commandLine.SvnLineCommand; -import org.jetbrains.idea.svn.config.SvnBindException; -import org.tigris.subversion.javahl.BindClientException; -import org.tigris.subversion.javahl.ClientException; +import org.tmatesoft.svn.core.SVNURL; import java.io.File; import java.util.*; @@ -42,7 +39,7 @@ import java.util.*; public class SvnCommitRunner { private final String myExePath; @Nullable private final AuthenticationCallback myAuthenticationCallback; - private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.svn.SvnCommitRunner"); + private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.svn.commandLine.SvnCommitRunner"); private SvnCommitRunner.CommandListener myCommandListener; public SvnCommitRunner(@NotNull String path, @Nullable CommitEventHandler handler, @Nullable AuthenticationCallback authenticationCallback) { @@ -57,7 +54,7 @@ public class SvnCommitRunner { boolean noUnlock, boolean keepChangelist, String[] changelists, - Map revpropTable) throws ClientException { + Map revpropTable, Convertor<String[], SVNURL> urlProvider) throws VcsException { if (paths.length == 0) return Revision.SVN_INVALID_REVNUM; final List<String> parameters = new ArrayList<String>(); @@ -84,19 +81,14 @@ public class SvnCommitRunner { Arrays.sort(paths); parameters.addAll(Arrays.asList(paths)); - try { - SvnLineCommand.runWithAuthenticationAttempt(myExePath, new File(paths[0]), SvnCommandName.ci, - myCommandListener, myAuthenticationCallback, ArrayUtil.toStringArray(parameters)); - } - catch (SvnBindException e) { - throw BindClientException.create(e, Revision.SVN_INVALID_REVNUM); - } + SvnLineCommand.runWithAuthenticationAttempt(myExePath, new File(paths[0]), urlProvider.convert(paths), SvnCommandName.ci, + myCommandListener, myAuthenticationCallback, ArrayUtil.toStringArray(parameters)); myCommandListener.throwExceptionIfOccurred(); return myCommandListener.getCommittedRevision(); } - private static class CommandListener extends LineCommandListener { + public static class CommandListener extends LineCommandListener { @Nullable private final CommitEventHandler myHandler; private SvnBindException myException; private long myCommittedRevision = Revision.SVN_INVALID_REVNUM; @@ -106,9 +98,9 @@ public class SvnCommitRunner { myHandler = handler; } - public void throwExceptionIfOccurred() throws BindClientException { + public void throwExceptionIfOccurred() throws VcsException { if (myException != null) { - throw BindClientException.create(myException, Revision.SVN_INVALID_REVNUM); + throw myException; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java index 022130e4a9f6..d8dbb6c19f67 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java @@ -18,6 +18,7 @@ package org.jetbrains.idea.svn.commandLine; import com.intellij.openapi.util.Getter; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Consumer; +import org.jetbrains.annotations.NotNull; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNNodeKind; @@ -49,7 +50,7 @@ public class SvnInfoHandler extends DefaultHandler { public SvnInfoHandler(File base, final Consumer<SVNInfo> infoConsumer) { myBase = base; myInfoConsumer = infoConsumer; - myPending = new SvnInfoStructure(); + myPending = createPending(); myElementsMap = new HashMap<String, Getter<ElementHandlerBase>>(); fillElements(); myParseStack = new ArrayList<ElementHandlerBase>(); @@ -70,7 +71,14 @@ public class SvnInfoHandler extends DefaultHandler { myInfoConsumer.consume(info); } myResultsMap.put(info.getFile(), info); - myPending = new SvnInfoStructure(); + myPending = createPending(); + } + + private SvnInfoStructure createPending() { + SvnInfoStructure pending = new SvnInfoStructure(); + pending.myDepth = SVNDepth.INFINITY; + + return pending; } @Override @@ -90,16 +98,13 @@ public class SvnInfoHandler extends DefaultHandler { public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { assertSAX(! myParseStack.isEmpty()); ElementHandlerBase current = myParseStack.get(myParseStack.size() - 1); - if (mySb.length() > 0) { - current.characters(mySb.toString().trim(), myPending); - mySb.setLength(0); - } while (true) { final boolean createNewChild = current.startElement(uri, localName, qName, attributes); if (createNewChild) { assertSAX(myElementsMap.containsKey(qName)); final ElementHandlerBase newChild = myElementsMap.get(qName).get(); + newChild.setParent(current); newChild.updateInfo(attributes, myPending); myParseStack.add(newChild); return; @@ -116,6 +121,18 @@ public class SvnInfoHandler extends DefaultHandler { } @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + ElementHandlerBase current = myParseStack.get(myParseStack.size() - 1); + String value = mySb.toString().trim(); + + if (!StringUtil.isEmpty(value)) { + current.characters(value, myPending); + } + + mySb.setLength(0); + } + + @Override public void characters(char[] ch, int start, int length) throws SAXException { assertSAX(! myParseStack.isEmpty()); mySb.append(ch, start, length); @@ -252,6 +269,24 @@ public class SvnInfoHandler extends DefaultHandler { return new Url(); } }); + myElementsMap.put("relative-url", new Getter<ElementHandlerBase>() { + @Override + public ElementHandlerBase get() { + return new RelativeUrl(); + } + }); + myElementsMap.put("lock", new Getter<ElementHandlerBase>() { + @Override + public ElementHandlerBase get() { + return new Lock(); + } + }); + myElementsMap.put("created", new Getter<ElementHandlerBase>() { + @Override + public ElementHandlerBase get() { + return new Date(); + } + }); myElementsMap.put("uuid", new Getter<ElementHandlerBase>() { @Override public ElementHandlerBase get() { @@ -270,6 +305,18 @@ public class SvnInfoHandler extends DefaultHandler { return new WcInfo(); } }); + myElementsMap.put("moved-to", new Getter<ElementHandlerBase>() { + @Override + public ElementHandlerBase get() { + return new MovedPath(); + } + }); + myElementsMap.put("moved-from", new Getter<ElementHandlerBase>() { + @Override + public ElementHandlerBase get() { + return new MovedPath(); + } + }); myElementsMap.put("wcroot-abspath", new Getter<ElementHandlerBase>() { @Override public ElementHandlerBase get() { @@ -295,6 +342,12 @@ public class SvnInfoHandler extends DefaultHandler { @Override protected void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException { + // TODO: Currently information for conflict (not tree-conflict) available in svn 1.8 is not used + // TODO: And it also not suite well for SVNKit api + if (getParent() instanceof Conflict) { + return; + } + final String side = attributes.getValue("side"); if ("source-left".equals(side)) { final SvnInfoStructure.ConflictVersion conflictVersion = new SvnInfoStructure.ConflictVersion(); @@ -382,7 +435,7 @@ public class SvnInfoHandler extends DefaultHandler { @Override public void characters(String s, SvnInfoStructure structure) throws SAXException { - structure.myConflictWorking = s; + structure.myConflictNew = new File(s).getName(); } } @@ -397,7 +450,7 @@ public class SvnInfoHandler extends DefaultHandler { @Override public void characters(String s, SvnInfoStructure structure) throws SAXException { - structure.myConflictNew = s; + structure.myConflictWorking = new File(s).getName(); } } @@ -412,14 +465,13 @@ public class SvnInfoHandler extends DefaultHandler { @Override public void characters(String s, SvnInfoStructure structure) throws SAXException { - // todo path? or plus base - structure.myConflictOld = s; + structure.myConflictOld = new File(s).getName(); } } private static class Conflict extends ElementHandlerBase { private Conflict() { - super(new String[]{"prev-base-file","prev-wc-file","cur-base-file","prop-file"}, new String[]{}); + super(new String[]{"prev-base-file","prev-wc-file","cur-base-file","prop-file"}, new String[]{"version"}); } @Override @@ -498,6 +550,25 @@ public class SvnInfoHandler extends DefaultHandler { } } + /** + * "moved-from" and "moved-to" elements are represented by this class. + */ + private static class MovedPath extends ElementHandlerBase { + + private MovedPath() { + super(new String[]{}, new String[]{}); + } + + @Override + protected void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException { + } + + @Override + public void characters(String s, SvnInfoStructure structure) throws SAXException { + // TODO: is there some field to initialize from this value? + } + } + private static class TextUpdated extends ElementHandlerBase { private TextUpdated() { super(new String[]{}, new String[]{}); @@ -617,7 +688,7 @@ public class SvnInfoHandler extends DefaultHandler { private static class WcInfo extends ElementHandlerBase { private WcInfo() { super(new String[]{"wcroot-abspath", "schedule", "depth", "text-updated", "checksum", "changelist", "copy-from-url", - "copy-from-rev"}, new String[]{}); + "copy-from-rev", "moved-to", "moved-from"}, new String[]{}); } @Override @@ -698,11 +769,34 @@ public class SvnInfoHandler extends DefaultHandler { } } + private static class RelativeUrl extends Url{ + @Override + public void characters(String s, SvnInfoStructure structure) throws SAXException { + structure.relativeUrl = s; + } + } + + private static class Lock extends ElementHandlerBase { + private Lock() { + super(new String[]{"created"}, new String[]{}); + } + + @Override + protected void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException { + // TODO: + } + + @Override + public void characters(String s, SvnInfoStructure structure) throws SAXException { + // TODO: + } + } + private static class Entry extends ElementHandlerBase { private final File myBase; private Entry(final File base) { - super(new String[]{"url","repository","wc-info","commit","conflict","tree-conflict"}, new String[]{}); + super(new String[]{"url", "relative-url", "lock", "repository","wc-info","commit","conflict","tree-conflict"}, new String[]{}); myBase = base; } @@ -763,12 +857,22 @@ public class SvnInfoHandler extends DefaultHandler { private abstract static class ElementHandlerBase { private final Set<String> myAwaitedChildren; private final Set<String> myAwaitedChildrenMultiple; + private ElementHandlerBase parent; ElementHandlerBase(String[] awaitedChildren, String[] awaitedChildrenMultiple) { myAwaitedChildren = new HashSet<String>(Arrays.asList(awaitedChildren)); myAwaitedChildrenMultiple = new HashSet<String>(Arrays.asList(awaitedChildrenMultiple)); } + @NotNull + public ElementHandlerBase getParent() { + return parent; + } + + public void setParent(@NotNull ElementHandlerBase parent) { + this.parent = parent; + } + protected abstract void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException; public boolean startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java index 0292b7d93614..1aa5248c1939 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java @@ -34,6 +34,7 @@ import java.util.Date; */ public class SvnInfoStructure { public File myFile; + public String relativeUrl; public SVNURL myUrl; public SVNURL myRootURL; public long myRevision; diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java index 5821d7a5fb18..c5e3b9e6861c 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java @@ -23,20 +23,30 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.LineHandlerHelper; import com.intellij.openapi.vcs.LineProcessEventListener; import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.ArrayUtil; import com.intellij.util.EventDispatcher; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.AuthenticationCallback; -import org.jetbrains.idea.svn.SvnBindUtil; -import org.jetbrains.idea.svn.config.SvnBindException; +import org.jetbrains.idea.svn.SvnUtil; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; +import org.tmatesoft.svn.core.auth.SVNAuthentication; +import org.tmatesoft.svn.core.auth.SVNPasswordAuthentication; +import org.tmatesoft.svn.core.auth.SVNSSLAuthentication; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Created with IntelliJ IDEA. @@ -47,11 +57,21 @@ import java.util.concurrent.atomic.AtomicReference; * honestly stolen from GitLineHandler */ public class SvnLineCommand extends SvnCommand { + public static final String AUTHENTICATION_REALM = "Authentication realm:"; public static final String CERTIFICATE_ERROR = "Error validating server certificate for"; public static final String PASSPHRASE_FOR = "Passphrase for"; public static final String UNABLE_TO_CONNECT = "svn: E170001:"; public static final String CANNOT_AUTHENTICATE_TO_PROXY = "Could not authenticate to proxy server"; + public static final String AUTHENTICATION_FAILED_MESSAGE = "Authentication failed"; + + private static final String INVALID_CREDENTIALS_FOR_SVN_PROTOCOL = "svn: E170001: Can't get"; + private static final String UNTRUSTED_SERVER_CERTIFICATE = "Server SSL certificate untrusted"; + private static final String ACCESS_TO_PREFIX = "Access to "; + private static final String FORBIDDEN_STATUS = "forbidden"; + private static final String PASSWORD_STRING = "password"; + + private static final Pattern UNABLE_TO_CONNECT_TO_URL_PATTERN = Pattern.compile("Unable to connect to a repository at URL '(.*)'"); // kept for exact text //public static final String CLIENT_CERTIFICATE_FILENAME = "Client certificate filename:"; @@ -66,6 +86,7 @@ public class SvnLineCommand extends SvnCommand { private final EventDispatcher<LineProcessEventListener> myLineListeners; private final AtomicReference<Integer> myExitCode; private final StringBuffer myErr; + private final StringBuffer myStdOut; public SvnLineCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath) { this(workingDirectory, commandName, exePath, null); @@ -76,6 +97,7 @@ public class SvnLineCommand extends SvnCommand { myLineListeners = EventDispatcher.create(LineProcessEventListener.class); myExitCode = new AtomicReference<Integer>(); myErr = new StringBuffer(); + myStdOut = new StringBuffer(); } @Override @@ -89,13 +111,14 @@ public class SvnLineCommand extends SvnCommand { } } - public static void runWithAuthenticationAttempt(final String exePath, - final File firstFile, - SvnCommandName commandName, - final LineCommandListener listener, - @Nullable AuthenticationCallback authenticationCallback, - final String... parameters) throws SvnBindException { - File base = firstFile.isDirectory() ? firstFile : firstFile.getParentFile(); + public static SvnLineCommand runWithAuthenticationAttempt(final String exePath, + final File firstFile, + final SVNURL url, + SvnCommandName commandName, + final LineCommandListener listener, + @Nullable AuthenticationCallback authenticationCallback, + String... parameters) throws SvnBindException { + File base = firstFile != null ? (firstFile.isDirectory() ? firstFile : firstFile.getParentFile()) : null; base = SvnBindUtil.correctUpToExistingParent(base); listener.baseDirectory(base); @@ -111,27 +134,35 @@ public class SvnLineCommand extends SvnCommand { while (true) { final SvnLineCommand command = runCommand(exePath, commandName, listener, base, configDir, parameters); - if (command.myErr.length() > 0) { - final String errText = command.myErr.toString().trim(); - if (authenticationCallback != null) { - final AuthCallbackCase callback = createCallback(errText, authenticationCallback, base); - if (callback != null) { - cleanup(exePath, commandName, base); - if (callback.getCredentials(errText)) { - if (authenticationCallback.getSpecialConfigDir() != null) { - configDir = authenticationCallback.getSpecialConfigDir(); + final Integer exitCode = command.myExitCode.get(); + + // could be situations when exit code = 0, but there is info "warning" in error stream + // for instance, for "svn status" on non-working copy folder + if (exitCode != 0) { + if (command.myErr.length() > 0) { + // handle authentication + final String errText = command.myErr.toString().trim(); + if (authenticationCallback != null) { + final AuthCallbackCase callback = createCallback(errText, authenticationCallback, base, url); + if (callback != null) { + cleanup(exePath, command, base); + if (callback.getCredentials(errText)) { + if (authenticationCallback.getSpecialConfigDir() != null) { + configDir = authenticationCallback.getSpecialConfigDir(); + } + parameters = updateParameters(callback, parameters); + continue; } - continue; } } + throw new SvnBindException(errText); + } else { + throw new SvnBindException("Svn process exited with error code: " + exitCode); } - throw new SvnBindException(errText); + } else if (command.myErr.length() > 0) { + LOG.info("Detected warning - " + command.myErr); } - final Integer exitCode = command.myExitCode.get(); - if (exitCode != 0) { - throw new SvnBindException("Svn process exited with error code: " + exitCode); - } - return; + return command; } } finally { if (authenticationCallback != null) { @@ -140,6 +171,13 @@ public class SvnLineCommand extends SvnCommand { } } + private static String[] updateParameters(AuthCallbackCase callback, String[] parameters) { + List<String> p = new ArrayList<String>(Arrays.asList(parameters)); + + callback.updateParameters(p); + return ArrayUtil.toStringArray(p); + } + private static void writeIdeaConfig2SubversionConfig(@NotNull AuthenticationCallback authenticationCallback, @NotNull File base) throws SvnBindException { if (authenticationCallback.haveDataForTmpConfig()) { try { @@ -158,7 +196,7 @@ public class SvnLineCommand extends SvnCommand { } } - private static AuthCallbackCase createCallback(final String errText, final AuthenticationCallback callback, final File base) { + private static AuthCallbackCase createCallback(final String errText, final AuthenticationCallback callback, final File base, final SVNURL url) { if (errText.startsWith(CERTIFICATE_ERROR)) { return new CertificateCallbackCase(callback, base); } @@ -171,9 +209,85 @@ public class SvnLineCommand extends SvnCommand { if (errText.startsWith(UNABLE_TO_CONNECT) && errText.contains(CANNOT_AUTHENTICATE_TO_PROXY)) { return new ProxyCallback(callback, base); } + // http/https protocol invalid credentials + if (errText.contains(AUTHENTICATION_FAILED_MESSAGE)) { + return new UsernamePasswordCallback(callback, base, url); + } + // messages could be "Can't get password", "Can't get username or password" + if (errText.contains(INVALID_CREDENTIALS_FOR_SVN_PROTOCOL) && errText.contains(PASSWORD_STRING)) { + // svn protocol invalid credentials + return new UsernamePasswordCallback(callback, base, url); + } + // https one-way protocol untrusted server certificate + if (errText.contains(UNTRUSTED_SERVER_CERTIFICATE)) { + return new CertificateCallbackCase(callback, base); + } + // https two-way protocol invalid client certificate + if (errText.contains(ACCESS_TO_PREFIX) && errText.contains(FORBIDDEN_STATUS)) { + return new TwoWaySslCallback(callback, base, url); + } return null; } + // Special callback for svn 1.8 credentials request as --non-interactive does not return + // authentication realm (just url) - so we could not create temp cache + private static class UsernamePasswordCallback extends AuthCallbackCase { + protected SVNAuthentication myAuthentication; + protected SVNURL myUrl; + + protected UsernamePasswordCallback(AuthenticationCallback callback, File base, SVNURL url) { + super(callback, base); + myUrl = url; + } + + @Override + boolean getCredentials(String errText) throws SvnBindException { + myAuthentication = myAuthenticationCallback.requestCredentials(myUrl != null ? myUrl : parseUrlFromError(errText), + getType()); + + return myAuthentication != null; + } + + public String getType() { + return ISVNAuthenticationManager.PASSWORD; + } + + @Override + public void updateParameters(List<String> parameters) { + if (myAuthentication instanceof SVNPasswordAuthentication) { + SVNPasswordAuthentication auth = (SVNPasswordAuthentication)myAuthentication; + + parameters.add("--username"); + parameters.add(auth.getUserName()); + parameters.add("--password"); + parameters.add(auth.getPassword()); + if (!auth.isStorageAllowed()) { + parameters.add("--no-auth-cache"); + } + } + } + + private SVNURL parseUrlFromError(String errorText) { + Matcher matcher = UNABLE_TO_CONNECT_TO_URL_PATTERN.matcher(errorText); + String urlValue = null; + + if (matcher.find()) { + urlValue = matcher.group(1); + } + + return urlValue != null ? parseUrl(urlValue) : null; + } + + private SVNURL parseUrl(String urlValue) { + try { + return SVNURL.parseURIEncoded(urlValue); + } + catch (SVNException e) { + return null; + } + } + } + private static class ProxyCallback extends AuthCallbackCase { protected ProxyCallback(AuthenticationCallback callback, File base) { super(callback, base); @@ -192,7 +306,8 @@ public class SvnLineCommand extends SvnCommand { @Override boolean getCredentials(String errText) throws SvnBindException { - final String realm = cutFirstLine(errText).substring(AUTHENTICATION_REALM.length()).trim(); + final String realm = + errText.startsWith(AUTHENTICATION_REALM) ? cutFirstLine(errText).substring(AUTHENTICATION_REALM.length()).trim() : null; final boolean isPassword = StringUtil.containsIgnoreCase(errText, "password"); if (myTried) { myAuthenticationCallback.clearPassiveCredentials(realm, myBase, isPassword); @@ -212,13 +327,16 @@ public class SvnLineCommand extends SvnCommand { } private static class CertificateCallbackCase extends AuthCallbackCase { + private boolean accepted; + private CertificateCallbackCase(AuthenticationCallback callback, File base) { super(callback, base); } @Override public boolean getCredentials(final String errText) throws SvnBindException { - String realm = cutFirstLine(errText).substring(CERTIFICATE_ERROR.length()); + // parse realm from error text + String realm = errText; final int idx1 = realm.indexOf('\''); if (idx1 == -1) { throw new SvnBindException("Can not detect authentication realm name: " + errText); @@ -229,11 +347,48 @@ public class SvnLineCommand extends SvnCommand { } realm = realm.substring(idx1 + 1, idx2); if (! myTried && myAuthenticationCallback.acceptSSLServerCertificate(myBase, realm)) { + accepted = true; myTried = true; return true; } throw new SvnBindException("Server SSL certificate rejected"); } + + @Override + public void updateParameters(List<String> parameters) { + if (accepted) { + parameters.add("--trust-server-cert"); + } + } + } + + private static class TwoWaySslCallback extends UsernamePasswordCallback { + + protected TwoWaySslCallback(AuthenticationCallback callback, File base, SVNURL url) { + super(callback, base, url); + } + + @Override + public String getType() { + return ISVNAuthenticationManager.SSL; + } + + @Override + public void updateParameters(List<String> parameters) { + if (myAuthentication instanceof SVNSSLAuthentication) { + SVNSSLAuthentication auth = (SVNSSLAuthentication)myAuthentication; + + // TODO: Seems that config option should be specified for concrete server and not for global group. + // as in that case it could be overriden by settings in config file + parameters.add("--config-option"); + parameters.add("servers:global:ssl-client-cert-file=" + auth.getCertificatePath()); + parameters.add("--config-option"); + parameters.add("servers:global:ssl-client-cert-password=" + auth.getPassword()); + if (!auth.isStorageAllowed()) { + parameters.add("--no-auth-cache"); + } + } + } } private static abstract class AuthCallbackCase { @@ -247,17 +402,23 @@ public class SvnLineCommand extends SvnCommand { } abstract boolean getCredentials(final String errText) throws SvnBindException; + + public void updateParameters(List<String> parameters) { + } } - private static void cleanup(String exePath, SvnCommandName commandName, File base) throws SvnBindException { - File wcRoot = SvnBindUtil.getWcRoot(base); - if (wcRoot == null) throw new SvnBindException("Can not find working copy root for: " + base.getPath()); + private static void cleanup(String exePath, SvnCommand command, File base) throws SvnBindException { + // TODO: could be issues with fake "empty" command as it is not writable - but only read commands currently use "empty" command + // TODO: and "empty" command will be removed shortly + if (command.isManuallyDestroyed() && command.getCommandName().isWriteable()) { + File wcRoot = SvnUtil.getWorkingCopyRootNew(base); + if (wcRoot == null) { + throw new SvnBindException("Can not find working copy root for: " + base.getPath()); + } - //cleanup -> check command type - if (commandName.isWriteable()) { - final SvnSimpleCommand command = new SvnSimpleCommand(wcRoot, SvnCommandName.cleanup, exePath); + final SvnSimpleCommand cleanupCommand = new SvnSimpleCommand(wcRoot, SvnCommandName.cleanup, exePath); try { - command.run(); + cleanupCommand.run(); } catch (VcsException e) { throw new SvnBindException(e); @@ -320,6 +481,10 @@ public class SvnLineCommand extends SvnCommand { command.addLineListener(new LineProcessEventListener() { @Override public void onLineAvailable(String line, Key outputType) { + if (ProcessOutputTypes.STDOUT.equals(outputType)) { + command.myStdOut.append(line); + } + if (SvnCommand.LOG.isDebugEnabled()) { SvnCommand.LOG.debug("==> " + line); } diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java index 75c43befb6cb..a088da0334a3 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java @@ -94,7 +94,11 @@ public class SvnSimpleCommand extends SvnCommand { if (myException != null) throw myException; final int code = getExitCode(); if (code == 0) { - return myStdout.toString(); + String result = myStdout.toString(); + + LOG.debug(result); + + return result; } else { final String msg = new StringBuilder("Svn process exited with error code: ").append(code).append("\n") .append("stderr: ").append(myStderr.toString()).append("\nstdout: ").append(getStdout().toString()) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UpdateOutputLineConverter.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UpdateOutputLineConverter.java index 6536646374bd..8700c363f460 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UpdateOutputLineConverter.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UpdateOutputLineConverter.java @@ -62,6 +62,8 @@ public class UpdateOutputLineConverter { } public SVNEvent convert(final String line) { + // TODO: Add direct processing of "Summary of conflicts" lines at the end of "svn update" output (if there are conflicts). + // TODO: Now it works ok because parseNormalLine could not determine necessary statuses from that and further lines if (StringUtil.isEmptyOrSpaces(line)) return null; if (line.startsWith(UPDATING)) { @@ -103,9 +105,9 @@ public class UpdateOutputLineConverter { if (line.length() < 5) return null; final char first = line.charAt(0); if (' ' != first && ! ourActions.contains(first)) return null; - final SVNStatusType contentsStatus = getStatusType(first); + final SVNStatusType contentsStatus = CommandUtil.getStatusType(first); final char second = line.charAt(1); - final SVNStatusType propertiesStatus = getStatusType(second); + final SVNStatusType propertiesStatus = CommandUtil.getStatusType(second); final char lock = line.charAt(2); // dont know what to do with stolen lock info if (' ' != lock && 'B' != lock) return null; final char treeConflict = line.charAt(3); @@ -140,28 +142,6 @@ public class UpdateOutputLineConverter { null, action, expectedAction, null, null, null, null, null); } - private SVNStatusType getStatusType(char first) { - final SVNStatusType contentsStatus; - if ('A' == first) { - contentsStatus = SVNStatusType.STATUS_ADDED; - } else if ('D' == first) { - contentsStatus = SVNStatusType.STATUS_DELETED; - } else if ('U' == first) { - contentsStatus = SVNStatusType.CHANGED; - } else if ('C' == first) { - contentsStatus = SVNStatusType.CONFLICTED; - } else if ('G' == first) { - contentsStatus = SVNStatusType.MERGED; - } else if ('R' == first) { - contentsStatus = SVNStatusType.STATUS_REPLACED; - } else if ('E' == first) { - contentsStatus = SVNStatusType.STATUS_OBSTRUCTED; - } else { - contentsStatus = SVNStatusType.STATUS_NORMAL; - } - return contentsStatus; - } - @Nullable private long matchAndGetRevision(final Pattern pattern, final String line) { final Matcher matcher = pattern.matcher(line); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/CmdConflictClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/CmdConflictClient.java new file mode 100644 index 000000000000..79614a24f554 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/CmdConflictClient.java @@ -0,0 +1,33 @@ +package org.jetbrains.idea.svn.conflict; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.tmatesoft.svn.core.SVNDepth; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdConflictClient extends BaseSvnClient implements ConflictClient { + + // TODO: Add possibility to resolve content conflicts separately from property conflicts. + @Override + public void resolve(@NotNull File path, boolean resolvePropertyConflicts) throws VcsException { + List<String> parameters = new ArrayList<String>(); + + CommandUtil.put(parameters, path); + CommandUtil.put(parameters, SVNDepth.EMPTY); + parameters.add("--accept"); + parameters.add("working"); + + // for now parsing of the output is not required as command is executed only for one file + // and will be either successful or exception will be thrown + CommandUtil.execute(myVcs, SvnCommandName.resolve, parameters, null); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/ConflictClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/ConflictClient.java new file mode 100644 index 000000000000..812fdef5621b --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/ConflictClient.java @@ -0,0 +1,15 @@ +package org.jetbrains.idea.svn.conflict; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.SvnClient; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface ConflictClient extends SvnClient { + + void resolve(@NotNull File path, boolean resolvePropertyConflicts) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/SvnKitConflictClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/SvnKitConflictClient.java new file mode 100644 index 000000000000..33ee0f870c95 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/SvnKitConflictClient.java @@ -0,0 +1,25 @@ +package org.jetbrains.idea.svn.conflict; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.SVNConflictChoice; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitConflictClient extends BaseSvnClient implements ConflictClient { + @Override + public void resolve(@NotNull File path, boolean resolvePropertyConflicts) throws VcsException { + try { + myVcs.createWCClient().doResolve(path, SVNDepth.EMPTY, true, resolvePropertyConflicts, SVNConflictChoice.MERGED); + } + catch (SVNException e) { + throw new VcsException(e); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/content/CmdContentClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/CmdContentClient.java new file mode 100644 index 000000000000..c822fe0a1f48 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/CmdContentClient.java @@ -0,0 +1,39 @@ +package org.jetbrains.idea.svn.content; + +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.impl.ContentRevisionCache; +import com.intellij.openapi.vfs.CharsetToolkit; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.*; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdContentClient extends BaseSvnClient implements ContentClient { + + @Override + public byte[] getContent(@NotNull SvnTarget target, @Nullable SVNRevision revision, @Nullable SVNRevision pegRevision) + throws VcsException, FileTooBigRuntimeException { + // TODO: rewrite this to provide output as Stream + // TODO: rewrite without conversion from String to byte[] + // TODO: Also implement max size constraint like in SvnKitContentClient + List<String> parameters = new ArrayList<String>(); + CommandUtil.put(parameters, target.getPathOrUrlString(), pegRevision); + CommandUtil.put(parameters, revision); + + SvnCommand command = CommandUtil.execute(myVcs, SvnCommandName.cat, parameters, null); + + byte[] bytes = CharsetToolkit.getUtf8Bytes(command.getOutput()); + + ContentRevisionCache.checkContentsSize(target.getPathOrUrlString(), bytes.length); + + return bytes; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/content/ContentClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/ContentClient.java new file mode 100644 index 000000000000..3654d09c88bc --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/ContentClient.java @@ -0,0 +1,17 @@ +package org.jetbrains.idea.svn.content; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +/** + * @author Konstantin Kolosovsky. + */ +public interface ContentClient extends SvnClient { + + byte[] getContent(@NotNull SvnTarget target, @Nullable SVNRevision revision, @Nullable SVNRevision pegRevision) + throws VcsException, FileTooBigRuntimeException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/content/FileTooBigRuntimeException.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/FileTooBigRuntimeException.java new file mode 100644 index 000000000000..0a0e585069a6 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/FileTooBigRuntimeException.java @@ -0,0 +1,7 @@ +package org.jetbrains.idea.svn.content; + +/** + * @author Konstantin Kolosovsky. + */ +public class FileTooBigRuntimeException extends RuntimeException { +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/content/SvnKitContentClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/SvnKitContentClient.java new file mode 100644 index 000000000000..d5916970e320 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/SvnKitContentClient.java @@ -0,0 +1,61 @@ +package org.jetbrains.idea.svn.content; + +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.impl.ContentRevisionCache; +import com.intellij.vcsUtil.VcsUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNWCClient; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitContentClient extends BaseSvnClient implements ContentClient { + + @Override + public byte[] getContent(@NotNull SvnTarget target, @Nullable SVNRevision revision, @Nullable SVNRevision pegRevision) + throws VcsException, FileTooBigRuntimeException { + final int maxSize = VcsUtil.getMaxVcsLoadedFileSize(); + ByteArrayOutputStream buffer = new ByteArrayOutputStream() { + @Override + public synchronized void write(int b) { + if (size() > maxSize) throw new FileTooBigRuntimeException(); + super.write(b); + } + + @Override + public synchronized void write(byte[] b, int off, int len) { + if (size() > maxSize) throw new FileTooBigRuntimeException(); + super.write(b, off, len); + } + + @Override + public synchronized void writeTo(OutputStream out) throws IOException { + if (size() > maxSize) throw new FileTooBigRuntimeException(); + super.writeTo(out); + } + }; + SVNWCClient wcClient = myVcs.createWCClient(); + try { + if (target.isURL()) { + wcClient.doGetFileContents(target.getURL(), pegRevision, revision, true, buffer); + } else { + wcClient.doGetFileContents(target.getFile(), pegRevision, revision, true, buffer); + } + ContentRevisionCache.checkContentsSize(target.getPathOrUrlString(), buffer.size()); + } catch (FileTooBigRuntimeException e) { + ContentRevisionCache.checkContentsSize(target.getPathOrUrlString(), buffer.size()); + } catch (SVNException e) { + throw new VcsException(e); + } + return buffer.toByteArray(); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CmdCopyMoveClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CmdCopyMoveClient.java new file mode 100644 index 000000000000..eceef14d994d --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CmdCopyMoveClient.java @@ -0,0 +1,30 @@ +package org.jetbrains.idea.svn.copy; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdCopyMoveClient extends BaseSvnClient implements CopyMoveClient { + + @Override + public void copy(@NotNull File src, @NotNull File dst, boolean makeParents, boolean isMove) throws VcsException { + List<String> parameters = new ArrayList<String>(); + + CommandUtil.put(parameters, src); + CommandUtil.put(parameters, dst); + CommandUtil.put(parameters, makeParents, "--parents"); + + // for now parsing of the output is not required as command is executed only for one file + // and will be either successful or exception will be thrown + CommandUtil.execute(myVcs, isMove ? SvnCommandName.move : SvnCommandName.copy, parameters, null); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CopyMoveClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CopyMoveClient.java new file mode 100644 index 000000000000..ec48107c8223 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CopyMoveClient.java @@ -0,0 +1,15 @@ +package org.jetbrains.idea.svn.copy; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.SvnClient; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface CopyMoveClient extends SvnClient { + + void copy(@NotNull File src, @NotNull File dst, boolean makeParents, boolean isMove) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/SvnKitCopyMoveClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/SvnKitCopyMoveClient.java new file mode 100644 index 000000000000..ea84bc6f0816 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/SvnKitCopyMoveClient.java @@ -0,0 +1,28 @@ +package org.jetbrains.idea.svn.copy; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.SVNCopySource; +import org.tmatesoft.svn.core.wc.SVNRevision; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitCopyMoveClient extends BaseSvnClient implements CopyMoveClient { + + @Override + public void copy(@NotNull File src, @NotNull File dst, boolean makeParents, boolean isMove) throws VcsException { + final SVNCopySource copySource = new SVNCopySource(isMove ? SVNRevision.UNDEFINED : SVNRevision.WORKING, SVNRevision.WORKING, src); + + try { + myVcs.createCopyClient().doCopy(new SVNCopySource[]{copySource}, dst, isMove, makeParents, true); + } + catch (SVNException e) { + throw new VcsException(e); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/CmdDeleteClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/CmdDeleteClient.java new file mode 100644 index 000000000000..5fc26acfb1d3 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/CmdDeleteClient.java @@ -0,0 +1,29 @@ +package org.jetbrains.idea.svn.delete; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdDeleteClient extends BaseSvnClient implements DeleteClient { + + @Override + public void delete(@NotNull File path, boolean force) throws VcsException { + List<String> parameters = new ArrayList<String>(); + + CommandUtil.put(parameters, path); + CommandUtil.put(parameters, force, "--force"); + + // for now parsing of the output is not required as command is executed only for one file + // and will be either successful or exception will be thrown + CommandUtil.execute(myVcs, SvnCommandName.delete, parameters, null); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/DeleteClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/DeleteClient.java new file mode 100644 index 000000000000..23a1cf97cb31 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/DeleteClient.java @@ -0,0 +1,15 @@ +package org.jetbrains.idea.svn.delete; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.SvnClient; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface DeleteClient extends SvnClient { + + void delete(@NotNull File path, boolean force) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/SvnKitDeleteClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/SvnKitDeleteClient.java new file mode 100644 index 000000000000..c97cfd2a44bb --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/SvnKitDeleteClient.java @@ -0,0 +1,24 @@ +package org.jetbrains.idea.svn.delete; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNException; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitDeleteClient extends BaseSvnClient implements DeleteClient { + + @Override + public void delete(@NotNull File path, boolean force) throws VcsException { + try { + myVcs.createWCClient().doDelete(path, force, false); + } + catch (SVNException e) { + throw new VcsException(e); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CreateBranchOrTagDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CreateBranchOrTagDialog.java index 62fc918d8a92..e1ad9157ce7b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CreateBranchOrTagDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CreateBranchOrTagDialog.java @@ -263,17 +263,11 @@ public class CreateBranchOrTagDialog extends DialogWrapper { super.init(); SvnVcs vcs = SvnVcs.getInstance(myProject); String revStr = ""; - try { - SVNWCClient client = vcs.createWCClient(); - SVNInfo info = client.doInfo(mySrcFile, SVNRevision.UNDEFINED); - if (info != null) { - mySrcURL = info.getURL() == null ? null : info.getURL().toString(); - revStr = String.valueOf(info.getRevision()); - myURL = mySrcURL; - } - } - catch (SVNException e) { - // + SVNInfo info = vcs.getInfo(mySrcFile); + if (info != null) { + mySrcURL = info.getURL() == null ? null : info.getURL().toString(); + revStr = String.valueOf(info.getRevision()); + myURL = mySrcURL; } if (myURL == null) { return; @@ -354,15 +348,8 @@ public class CreateBranchOrTagDialog extends DialogWrapper { return true; } else if (myWorkingCopyRadioButton.isSelected()) { - String srcUrl; - try { - SVNWCClient client = SvnVcs.getInstance(myProject).createWCClient(); - SVNInfo info = client.doInfo(mySrcFile, SVNRevision.UNDEFINED); - srcUrl = info != null && info.getURL() != null ? info.getURL().toString() : null; - } - catch (SVNException e) { - srcUrl = null; - } + SVNInfo info = SvnVcs.getInstance(myProject).getInfo(mySrcFile); + String srcUrl = info != null && info.getURL() != null ? info.getURL().toString() : null; if (srcUrl == null) { myErrorLabel.setText(SvnBundle.message("create.branch.no.working.copy.error", myWorkingCopyField.getText())); return false; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CmdHistoryClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CmdHistoryClient.java new file mode 100644 index 000000000000..3948ddee57b7 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CmdHistoryClient.java @@ -0,0 +1,223 @@ +package org.jetbrains.idea.svn.history; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.LineSeparator; +import com.intellij.util.containers.hash.HashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.jetbrains.idea.svn.commandLine.SvnLineCommand; +import org.tmatesoft.svn.core.ISVNLogEntryHandler; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNLogEntry; +import org.tmatesoft.svn.core.SVNLogEntryPath; +import org.tmatesoft.svn.core.wc.SVNRevision; + +import java.io.File; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdHistoryClient extends BaseSvnClient implements HistoryClient { + + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.history.CmdHistoryClient"); + + @Override + public void doLog(@NotNull File path, + @NotNull SVNRevision startRevision, + @NotNull SVNRevision endRevision, + @Nullable SVNRevision pegRevision, + boolean stopOnCopy, + boolean discoverChangedPaths, + boolean includeMergedRevisions, + long limit, + @Nullable String[] revisionProperties, + @Nullable ISVNLogEntryHandler handler) throws VcsException { + // TODO: add revision properties parameter if necessary + // TODO: svn log command supports --xml option - could update parsing to use xml format + + // TODO: after merge remove setting includeMergedRevisions to false and update parsing + includeMergedRevisions = false; + + List<String> parameters = + prepareCommand(path, startRevision, endRevision, pegRevision, stopOnCopy, discoverChangedPaths, includeMergedRevisions, limit); + + try { + SvnLineCommand command = CommandUtil.runSimple(SvnCommandName.log, myVcs, path, null, parameters); + // TODO: handler should be called in parallel with command execution, but this will be in other thread + // TODO: check if that is ok for current handler implementation + parseOutput(handler, command); + } + catch (SVNException e) { + throw new VcsException(e); + } + } + + private static void parseOutput(@Nullable ISVNLogEntryHandler handler, @NotNull SvnLineCommand command) + throws VcsException, SVNException { + Parser parser = new Parser(handler); + for (String line : StringUtil.splitByLines(command.getOutput(), false)) { + parser.onLine(line); + } + } + + private static List<String> prepareCommand(@NotNull File path, + @NotNull SVNRevision startRevision, + @NotNull SVNRevision endRevision, + @Nullable SVNRevision pegRevision, + boolean stopOnCopy, boolean discoverChangedPaths, boolean includeMergedRevisions, long limit) { + List<String> parameters = new ArrayList<String>(); + + CommandUtil.put(parameters, path, pegRevision); + parameters.add("--revision"); + parameters.add(startRevision + ":" + endRevision); + + CommandUtil.put(parameters, stopOnCopy, "--stop-on-copy"); + CommandUtil.put(parameters, discoverChangedPaths, "--verbose"); + CommandUtil.put(parameters, includeMergedRevisions, "--use-merge-history"); + if (limit > 0) { + parameters.add("--limit"); + parameters.add(String.valueOf(limit)); + } + + return parameters; + } + + private static class Parser { + private static final String REVISION = "\\s*r(\\d+)\\s*"; + private static final String AUTHOR = "\\s*([^|]*)\\s*"; + private static final String DATE = "\\s*([^|]*)\\s*"; + private static final String MESSAGE_LINES = "\\s*(\\d+).*"; + + private static final Pattern ENTRY_START = Pattern.compile("-+"); + private static final Pattern DETAILS = Pattern.compile(REVISION + "\\|" + AUTHOR + "\\|" + DATE + "\\|" + MESSAGE_LINES); + + private static final String STATUS = "\\s*(\\w)"; + private static final String PATH = "\\s*(.*?)\\s*"; + private static final String COPY_FROM_PATH = "(/[^:]*)"; + private static final String COPY_FROM_REVISION = "(\\d+)\\))\\s*"; + private static final String COPY_FROM_INFO = "((\\(from " + COPY_FROM_PATH + ":" + COPY_FROM_REVISION + ")?"; + private static final Pattern CHANGED_PATH = Pattern.compile(STATUS + PATH + COPY_FROM_INFO); + + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + + ISVNLogEntryHandler handler; + + Entry entry; + boolean waitDetails; + boolean waitChangedPath; + boolean waitMessage; + + public Parser(@Nullable ISVNLogEntryHandler handler) { + this.handler = handler; + } + + public void onLine(@NotNull String line) throws VcsException, SVNException { + if (ENTRY_START.matcher(line).matches()) { + processEntryStart(); + } + else if (waitDetails) { + processDetails(line); + } + else if (waitMessage) { + processMessage(line); + } + else if (StringUtil.isEmpty(line.trim())) { + processChangedPathsFinished(); + } + else if (line.startsWith("Changed paths:")) { + processChangedPathsStarted(); + } + else if (waitChangedPath) { + processChangedPath(line); + } + else { + throw new VcsException("unknown state on line " + line); + } + } + + private void processChangedPath(@NotNull String line) throws VcsException { + Matcher matcher = CHANGED_PATH.matcher(line); + if (!matcher.matches()) { + throw new VcsException("changed path not found in " + line); + } + + String path = matcher.group(2); + char type = CommandUtil.getStatusChar(matcher.group(1)); + String copyPath = matcher.group(5); + long copyRevision = !StringUtil.isEmpty(matcher.group(6)) ? Long.valueOf(matcher.group(6)) : 0; + + entry.changedPaths.put(path, new SVNLogEntryPath(path, type, copyPath, copyRevision)); + } + + private void processChangedPathsStarted() { + waitChangedPath = true; + } + + private void processChangedPathsFinished() { + waitChangedPath = false; + waitMessage = true; + } + + private void processMessage(@NotNull String line) { + entry.message.append(line); + entry.message.append(LineSeparator.LF.getSeparatorString()); + } + + private void processDetails(@NotNull String line) throws VcsException { + Matcher matcher = DETAILS.matcher(line); + if (!matcher.matches()) { + throw new VcsException("details not found in " + line); + } + entry.revision = Long.valueOf(matcher.group(1)); + entry.author = matcher.group(2).trim(); + entry.date = tryGetDate(matcher.group(3)); + + waitDetails = false; + } + + private void processEntryStart() throws SVNException { + if (entry != null) { + handler.handleLogEntry(entry.toLogEntry()); + } + entry = new Entry(); + waitDetails = true; + waitMessage = false; + } + + private static Date tryGetDate(@NotNull String value) { + Date result = null; + try { + result = DATE_FORMAT.parse(value); + } + catch (ParseException e) { + LOG.debug(e); + } + return result; + } + + private static class Entry { + Map<String, SVNLogEntryPath> changedPaths = new HashMap<String, SVNLogEntryPath>(); + long revision; + String author; + Date date; + StringBuilder message = new StringBuilder(); + + private SVNLogEntry toLogEntry() { + return new SVNLogEntry(changedPaths, revision, author, date, message.toString()); + } + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/HistoryClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/HistoryClient.java new file mode 100644 index 000000000000..87e5e76556fa --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/HistoryClient.java @@ -0,0 +1,28 @@ +package org.jetbrains.idea.svn.history; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.ISVNLogEntryHandler; +import org.tmatesoft.svn.core.wc.SVNRevision; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface HistoryClient extends SvnClient { + + // TODO: Url is also supported as parameter in cmd - use Target class + void doLog(@NotNull File path, + @NotNull SVNRevision startRevision, + @NotNull SVNRevision endRevision, + @Nullable SVNRevision pegRevision, + boolean stopOnCopy, + boolean discoverChangedPaths, + boolean includeMergedRevisions, + long limit, + @Nullable String[] revisionProperties, + @Nullable ISVNLogEntryHandler handler) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java index 7ef0b2f3d0b6..3552853a8479 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java @@ -518,9 +518,8 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi revisionBefore = SVNRevision.create(revision); svnurl = SVNURL.parseURIEncoded(url); - final SVNWCClient client = myVcs.createWCClient(); - final SVNInfo info = client.doInfo(svnurl, SVNRevision.UNDEFINED, SVNRevision.HEAD); - targetInfo = client.doInfo(new File(file.getPath()), SVNRevision.UNDEFINED); + final SVNInfo info = myVcs.getInfo(svnurl, SVNRevision.HEAD); + targetInfo = myVcs.getInfo(new File(file.getPath())); if (info == null) { throw new VcsException("Can not get repository URL"); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java index 5ca1150869ae..d67e29b598a0 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java @@ -137,7 +137,7 @@ public class SvnEditCommitMessageAction extends AnAction { final String url = myLocation.getURL(); final SVNURL root; try { - root = SvnUtil.getRepositoryRoot(myVcs, SVNURL.parseURIEncoded(url), true); + root = SvnUtil.getRepositoryRoot(myVcs, SVNURL.parseURIEncoded(url)); if (root == null) { myException = new VcsException("Can not determine repository root for URL: " + url); return; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnFileRevision.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnFileRevision.java index bcb40a034ac9..046e16c148a4 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnFileRevision.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnFileRevision.java @@ -33,6 +33,7 @@ import org.jetbrains.idea.svn.SvnUtil; import org.jetbrains.idea.svn.SvnVcs; import org.tmatesoft.svn.core.SVNLogEntry; import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.IOException; import java.nio.charset.Charset; @@ -211,7 +212,7 @@ public class SvnFileRevision implements VcsFileRevision { } try { - myContents = SvnUtil.getFileContents(myVCS, myURL, true, myRevision, myPegRevision); + myContents = SvnUtil.getFileContents(myVCS, SvnTarget.fromURL(SvnUtil.parseUrl(myURL)), myRevision, myPegRevision); } catch (VcsException e) { myException = e; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java index 4a2c8725a898..860b646947f5 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java @@ -42,7 +42,6 @@ import org.jetbrains.idea.svn.*; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; -import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.util.SVNLogType; @@ -50,7 +49,6 @@ import javax.swing.*; import javax.swing.table.TableCellRenderer; import java.awt.*; import java.awt.event.MouseEvent; -import java.io.File; import java.nio.charset.Charset; import java.util.Collections; import java.util.Date; @@ -285,18 +283,9 @@ public class SvnHistoryProvider @Override protected void preliminary() throws SVNException { - SVNWCClient wcClient = myVcs.createWCClient(); - myInfo = wcClient.doInfo(new File(myFile.getIOFile().getAbsolutePath()), SVNRevision.UNDEFINED); - wcClient.setEventHandler(new ISVNEventHandler() { - public void handleEvent(SVNEvent event, double progress) throws SVNException { - } - - public void checkCancelled() throws SVNCancelException { - myPI.checkCanceled(); - } - }); + myInfo = myVcs.getInfo(myFile.getIOFile()); if (myInfo == null || myInfo.getRepositoryRootURL() == null) { - myException = new VcsException("File ''{0}'' is not under version control" + myFile.getIOFile()); + myException = new VcsException("File " + myFile.getPath() + " is not under version control"); return; } if (myInfo.getURL() == null) { @@ -319,14 +308,15 @@ public class SvnHistoryProvider myPI.setText2(SvnBundle.message("progress.text2.changes.establishing.connection", myUrl)); } final SVNRevision pegRevision = myInfo.getRevision(); - SVNLogClient client = myVcs.createLogClient(); try { - // a bug noticed when testing: we should pass "limit + 1" to get "limit" rows - client - .doLog(new File[]{new File(myFile.getIOFile().getAbsolutePath())}, - myFrom == null ? SVNRevision.HEAD : myFrom, myTo == null ? SVNRevision.create(1) : myTo, myPeg, - false, true, myShowMergeSources && mySupport15, myLimit + 1, null, - new MyLogEntryHandler(myVcs, myUrl, pegRevision, relativeUrl, createConsumerAdapter(myConsumer), repoRootURL, myFile.getCharset())); + myVcs.getFactory(myFile.getIOFile()).createHistoryClient().doLog( + myFile.getIOFile(), + myFrom == null ? SVNRevision.HEAD : myFrom, + myTo == null ? SVNRevision.create(1) : myTo, myPeg, + false, true, myShowMergeSources && mySupport15, myLimit + 1, null, + new MyLogEntryHandler(myVcs, myUrl, pegRevision, relativeUrl, + createConsumerAdapter(myConsumer), + repoRootURL, myFile.getCharset())); } catch (SVNCancelException e) { // @@ -383,7 +373,6 @@ public class SvnHistoryProvider } } - SVNWCClient wcClient = myVcs.createWCClient(); final SVNURL svnurl = SVNURL.parseURIEncoded(myUrl); SVNRevision operationalFrom = myFrom == null ? SVNRevision.HEAD : myFrom; final SVNURL rootURL = getRepositoryRoot(svnurl, myFrom); @@ -395,6 +384,7 @@ public class SvnHistoryProvider if (myUrl.startsWith(root)) { relativeUrl = myUrl.substring(root.length()); } + // TODO: Update this call to myVcs.getFactory.createHistoryClient SVNLogClient client = myVcs.createLogClient(); // a bug noticed when testing: we should pass "limit + 1" to get "limit" rows client.doLog(svnurl, new String[]{}, myPeg == null ? myFrom : myPeg, @@ -420,6 +410,7 @@ public class SvnHistoryProvider relativeUrl = myUrl.substring(root.length()); } + // TODO: Update this call to myVcs.getFactory.createHistoryClient SVNLogClient client = myVcs.createLogClient(); final RepositoryLogEntryHandler repositoryLogEntryHandler = @@ -437,36 +428,15 @@ public class SvnHistoryProvider } private SVNURL getRepositoryRoot(SVNURL svnurl, SVNRevision operationalFrom) throws SVNException { - final SVNRepository repository = myVcs.createRepository(svnurl); - final SVNURL root = repository.getRepositoryRoot(false); - if (root == null) { - return repository.getRepositoryRoot(true); - } - return root; - /*final SVNWCClient wcClient = myVcs.createWCClient(); - try { - final SVNInfo info; - info = wcClient.doInfo(svnurl, myPeg, operationalFrom); - return info.getRepositoryRootURL(); - } - catch (SVNException e) { - try { - final SVNInfo info; - info = wcClient.doInfo(svnurl, SVNRevision.UNDEFINED, SVNRevision.UNDEFINED); - return info.getRepositoryRootURL(); - } catch (SVNException e1) { - final SVNInfo info; - info = wcClient.doInfo(svnurl, SVNRevision.UNDEFINED, SVNRevision.HEAD); - return info.getRepositoryRootURL(); - } - }*/ + SVNInfo info = myVcs.getInfo(svnurl, SVNRevision.HEAD); + + return info != null ? info.getRepositoryRootURL() : null; } private boolean existsNow(SVNURL svnurl) { - final SVNWCClient wcClient = myVcs.createWCClient(); final SVNInfo info; try { - info = wcClient.doInfo(svnurl, SVNRevision.HEAD, SVNRevision.HEAD); + info = myVcs.getInfo(svnurl, SVNRevision.HEAD, SVNRevision.HEAD, null); } catch (SVNException e) { return false; @@ -493,6 +463,7 @@ public class SvnHistoryProvider protected final SvnPathThroughHistoryCorrection myLastPathCorrector; private final Charset myCharset; protected final ThrowableConsumer<VcsFileRevision, SVNException> myResult; + private final String myLastPath; private VcsFileRevision myPrevious; private final SVNRevision myPegRevision; protected final String myUrl; @@ -512,6 +483,7 @@ public class SvnHistoryProvider throws SVNException, VcsException { myVcs = vcs; myLastPathCorrector = new SvnPathThroughHistoryCorrection(lastPath); + myLastPath = lastPath; myCharset = charset; myIndicator = ProgressManager.getInstance().getProgressIndicator(); myResult = result; @@ -610,9 +582,9 @@ public class SvnHistoryProvider String author = logEntry.getAuthor(); String message = logEntry.getMessage(); SVNRevision rev = SVNRevision.create(logEntry.getRevision()); -// final SVNURL url = myRepositoryRoot.appendPath(myLastPath, true); - final SVNURL url = entryPath != null ? myRepositoryRoot.appendPath(entryPath.getPath(), true) : - myRepositoryRoot.appendPath(myLastPathCorrector.getBefore(), false); + final SVNURL url = myRepositoryRoot.appendPath(myLastPath, true); +// final SVNURL url = entryPath != null ? myRepositoryRoot.appendPath(entryPath.getPath(), true) : +// myRepositoryRoot.appendPath(myLastPathCorrector.getBefore(), false); return new SvnFileRevision(myVcs, myPegRevision, rev, url.toString(), author, date, message, copyPath, myCharset); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java index e30f8ca216cb..171b39cad623 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java @@ -20,21 +20,19 @@ import com.intellij.openapi.vcs.history.*; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnRevisionNumber; import org.jetbrains.idea.svn.SvnVcs; -import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNRevision; -import org.tmatesoft.svn.core.wc.SVNWCClient; import java.io.File; import java.util.List; /** -* Created with IntelliJ IDEA. -* User: Irina.Chernushina -* Date: 4/27/12 -* Time: 12:24 PM -* To change this template use File | Settings | File Templates. -*/ + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 4/27/12 + * Time: 12:24 PM + * To change this template use File | Settings | File Templates. + */ public class SvnHistorySession extends VcsAbstractHistorySession { private final SvnVcs myVcs; private final FilePath myCommittedPath; @@ -71,19 +69,8 @@ public class SvnHistorySession extends VcsAbstractHistorySession { } public static VcsRevisionNumber getCurrentCommittedRevision(final SvnVcs vcs, final File file) { - try { - SVNWCClient wcClient = vcs.createWCClient(); - SVNInfo info = wcClient.doInfo(file, SVNRevision.UNDEFINED); - if (info != null) { - return new SvnRevisionNumber(info.getCommittedRevision()); - } - else { - return null; - } - } - catch (SVNException e) { - return null; - } + SVNInfo info = vcs.getInfo(file); + return info != null ? new SvnRevisionNumber(info.getCommittedRevision()) : null; } public FilePath getCommittedPath() { @@ -101,7 +88,8 @@ public class SvnHistorySession extends VcsAbstractHistorySession { @Override public VcsHistorySession copy() { - return new SvnHistorySession(myVcs, getRevisionList(), myCommittedPath, myHaveMergeSources, getCurrentRevisionNumber(), true, myHasLocalSource); + return new SvnHistorySession(myVcs, getRevisionList(), myCommittedPath, myHaveMergeSources, getCurrentRevisionNumber(), true, + myHasLocalSource); } @Override diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnKitHistoryClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnKitHistoryClient.java new file mode 100644 index 000000000000..6d412853400e --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnKitHistoryClient.java @@ -0,0 +1,40 @@ +package org.jetbrains.idea.svn.history; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.ISVNLogEntryHandler; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.SVNLogClient; +import org.tmatesoft.svn.core.wc.SVNRevision; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitHistoryClient extends BaseSvnClient implements HistoryClient { + @Override + public void doLog(@NotNull File path, + @NotNull SVNRevision startRevision, + @NotNull SVNRevision endRevision, + @Nullable SVNRevision pegRevision, + boolean stopOnCopy, + boolean discoverChangedPaths, + boolean includeMergedRevisions, + long limit, + @Nullable String[] revisionProperties, + @Nullable ISVNLogEntryHandler handler) throws VcsException { + try { + // TODO: a bug noticed when testing: we should pass "limit + 1" to get "limit" rows + SVNLogClient client = myVcs.createLogClient(); + + client.doLog(new File[]{path}, startRevision, endRevision, pegRevision, stopOnCopy, discoverChangedPaths, includeMergedRevisions, + limit, revisionProperties, handler); + } + catch (SVNException e) { + throw new VcsException(e); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryContentRevision.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryContentRevision.java index a668a54f953a..b01308f76c13 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryContentRevision.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryContentRevision.java @@ -38,13 +38,10 @@ import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.impl.ContentRevisionCache; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.SvnBundle; -import org.jetbrains.idea.svn.SvnRevisionNumber; -import org.jetbrains.idea.svn.SvnUtil; -import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.*; import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -108,7 +105,7 @@ public class SvnRepositoryContentRevision implements ContentRevision, MarkerVcsC else { loader.run(); } - final SVNException exception = loader.getException(); + final Exception exception = loader.getException(); if (exception != null) { throw new VcsException(exception); } @@ -148,7 +145,7 @@ public class SvnRepositoryContentRevision implements ContentRevision, MarkerVcsC private final String myPath; private final long myRevision; private final OutputStream myDst; - private SVNException myException; + private Exception myException; public ContentLoader(String path, OutputStream dst, long revision) { myPath = path; @@ -156,7 +153,7 @@ public class SvnRepositoryContentRevision implements ContentRevision, MarkerVcsC myRevision = revision; } - public SVNException getException() { + public Exception getException() { return myException; } @@ -166,16 +163,16 @@ public class SvnRepositoryContentRevision implements ContentRevision, MarkerVcsC progress.setText(SvnBundle.message("progress.text.loading.contents", myPath)); progress.setText2(SvnBundle.message("progress.text2.revision.information", myRevision)); } + try { - SVNRepository repository = myVcs.createRepository(getFullPath()); - try { - repository.getFile("", myRevision, null, myDst); - } - finally { - repository.closeSession(); - } + byte[] contents = SvnUtil.getFileContents(myVcs, SvnTarget.fromURL(SvnUtil.parseUrl(getFullPath())), SVNRevision.create(myRevision), + SVNRevision.UNDEFINED); + myDst.write(contents); } - catch (SVNException e) { + catch (VcsException e) { + myException = e; + } + catch (IOException e) { myException = e; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/IntegratedSelectedOptionsDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/IntegratedSelectedOptionsDialog.java index 78718a7a0264..bfb9f1ff22c3 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/IntegratedSelectedOptionsDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/IntegratedSelectedOptionsDialog.java @@ -251,19 +251,10 @@ public class IntegratedSelectedOptionsDialog extends DialogWrapper { @Nullable private static SVNURL realTargetUrl(final SvnVcs vcs, final WorkingCopyInfo info, final String targetBranchUrl) { - final SVNWCClient client = vcs.createWCClient(); - try { - final SVNInfo svnInfo = client.doInfo(new File(info.getLocalPath()), SVNRevision.UNDEFINED); - final SVNURL svnurl = svnInfo.getURL(); + final SVNInfo svnInfo = vcs.getInfo(info.getLocalPath()); + final SVNURL svnurl = svnInfo != null ? svnInfo.getURL() : null; - if ((svnurl != null) && (svnurl.toString().startsWith(targetBranchUrl))) { - return svnurl; - } - } - catch (SVNException e) { - // tracked by return value - } - return null; + return (svnurl != null) && (svnurl.toString().startsWith(targetBranchUrl)) ? svnurl : null; } @Nullable diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java index b6c6a898b85a..bc08fff144dc 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java @@ -238,12 +238,7 @@ public class BranchInfo { } private SVNInfo getInfo(final File pathFile) { - try { - return myClient.doInfo(pathFile, SVNRevision.UNDEFINED); - } catch (SVNException e) { - // - } - return null; + return myVcs.getInfo(pathFile); } private SvnMergeInfoCache.MergeCheckResult checkPathGoingUp(final long revisionAsked, final long targetRevision, final String branchRootPath, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java index 184d5598f9fe..9799cf7ae4a4 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java @@ -117,6 +117,7 @@ public class PortableStatus extends SVNStatus { return null; } }; + // TODO: Update working copy format detection setWorkingCopyFormat(WorkingCopyFormat.ONE_DOT_SEVEN.getFormat()); } @@ -242,6 +243,30 @@ public class PortableStatus extends SVNStatus { } @Override + public SVNURL getURL() { + SVNURL url = super.getURL(); + + if (url == null) { + SVNInfo info = initInfo(); + url = info != null ? info.getURL() : url; + } + + return url; + } + + @Override + public File getFile() { + File file = super.getFile(); + + if (file == null) { + SVNInfo info = initInfo(); + file = info != null ? info.getFile() : file; + } + + return file; + } + + @Override public SVNRevision getRevision() { final SVNRevision revision = super.getRevision(); if (revision != null && revision.isValid()) return revision; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java new file mode 100644 index 000000000000..4720b33d9d37 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java @@ -0,0 +1,65 @@ +package org.jetbrains.idea.svn.properties; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommand; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.tmatesoft.svn.core.SVNPropertyValue; +import org.tmatesoft.svn.core.wc.SVNInfo; +import org.tmatesoft.svn.core.wc.SVNPropertyData; +import org.tmatesoft.svn.core.wc.SVNRevision; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { + + @Nullable + @Override + public SVNPropertyData getProperty(@NotNull File path, + @NotNull String property, + boolean revisionProperty, + @Nullable SVNRevision pegRevision, + @Nullable SVNRevision revision) + throws VcsException { + List<String> parameters = new ArrayList<String>(); + + parameters.add(property); + CommandUtil.put(parameters, path, pegRevision); + if (!revisionProperty) { + CommandUtil.put(parameters, revision); + } else { + parameters.add("--revprop"); + CommandUtil.put(parameters, resolveRevisionNumber(path, revision)); + } + + SvnCommand command = CommandUtil.execute(myVcs, SvnCommandName.propget, parameters, null); + + return new SVNPropertyData(property, SVNPropertyValue.create(StringUtil.nullize(command.getOutput())), null); + } + + private SVNRevision resolveRevisionNumber(@NotNull File path, @Nullable SVNRevision revision) throws VcsException { + long result = revision != null ? revision.getNumber() : -1; + + // base should be resolved manually - could not set revision to BASE to get revision property + if (SVNRevision.BASE.equals(revision)) { + SVNInfo info = myVcs.getInfo(path, SVNRevision.BASE); + + result = info != null ? info.getRevision().getNumber() : -1; + } + + if (result == -1) { + throw new VcsException("Could not determine revision number for file " + path + " and revision " + revision); + } + + return SVNRevision.create(result); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java new file mode 100644 index 000000000000..118a397eb23e --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java @@ -0,0 +1,23 @@ +package org.jetbrains.idea.svn.properties; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.wc.SVNPropertyData; +import org.tmatesoft.svn.core.wc.SVNRevision; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface PropertyClient extends SvnClient { + + @Nullable + SVNPropertyData getProperty(@NotNull final File path, + @NotNull final String property, + boolean revisionProperty, + @Nullable SVNRevision pegRevision, + @Nullable SVNRevision revision) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java new file mode 100644 index 000000000000..1c3c23619074 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java @@ -0,0 +1,68 @@ +package org.jetbrains.idea.svn.properties; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.wc.ISVNPropertyHandler; +import org.tmatesoft.svn.core.wc.SVNPropertyData; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNWCClient; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClient { + + @Nullable + @Override + public SVNPropertyData getProperty(@NotNull File path, + @NotNull String property, + boolean revisionProperty, + @Nullable SVNRevision pegRevision, + @Nullable SVNRevision revision) throws VcsException { + try { + if (!revisionProperty) { + return myVcs.createWCClient().doGetProperty(path, property, pegRevision, revision); + } else { + return getRevisionProperty(path, property, revision); + } + } + catch (SVNException e) { + throw new VcsException(e); + } + } + + private SVNPropertyData getRevisionProperty(@NotNull File path, @NotNull final String property, @Nullable SVNRevision revision) throws SVNException{ + final SVNWCClient client = myVcs.createWCClient(); + final SVNPropertyData[] result = new SVNPropertyData[1]; + + client.doGetRevisionProperty(path, null, revision, new ISVNPropertyHandler() { + @Override + public void handleProperty(File path, SVNPropertyData property) throws SVNException { + handle(property); + } + + @Override + public void handleProperty(SVNURL url, SVNPropertyData property) throws SVNException { + handle(property); + } + + @Override + public void handleProperty(long revision, SVNPropertyData property) throws SVNException { + handle(property); + } + + private void handle(@NotNull SVNPropertyData data) { + if (property.equals(data.getName())) { + result[0] = data; + } + } + }); + return result[0]; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/CmdRevertClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/CmdRevertClient.java new file mode 100644 index 000000000000..53c998f001a6 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/CmdRevertClient.java @@ -0,0 +1,79 @@ +package org.jetbrains.idea.svn.revert; + +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.containers.Convertor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.api.FileStatusResultParser; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNEvent; +import org.tmatesoft.svn.core.wc.SVNEventAction; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdRevertClient extends BaseSvnClient implements RevertClient { + + private static final String STATUS = "\\s*(.+?)\\s*"; + private static final String PATH = "\\s*\'(.*?)\'\\s*"; + private static final String OPTIONAL_COMMENT = "(.*)"; + private static final Pattern CHANGED_PATH = Pattern.compile(STATUS + PATH + OPTIONAL_COMMENT); + + @Override + public void revert(@NotNull File[] paths, @Nullable SVNDepth depth, @Nullable ISVNEventHandler handler) throws VcsException { + List<String> parameters = prepareParameters(paths, depth); + + // TODO: handler should be called in parallel with command execution, but this will be in other thread + // TODO: check if that is ok for current handler implementation + // TODO: add possibility to invoke "handler.checkCancelled" - process should be killed + CommandUtil + .execute(myVcs, SvnCommandName.revert, parameters, new FileStatusResultParser(CHANGED_PATH, handler, new RevertStatusConvertor())); + } + + private static List<String> prepareParameters(File[] paths, SVNDepth depth) { + ArrayList<String> parameters = new ArrayList<String>(); + + CommandUtil.put(parameters, paths); + CommandUtil.put(parameters, depth); + + return parameters; + } + + private static class RevertStatusConvertor implements Convertor<Matcher, SVNEvent> { + + public SVNEvent convert(@NotNull Matcher matcher) { + String statusMessage = matcher.group(1); + String path = matcher.group(2); + + return new SVNEvent(new File(path), null, null, 0, null, null, null, null, createAction(statusMessage), null, null, null, null, null, + null); + } + + @Nullable + public static SVNEventAction createAction(@NotNull String code) { + SVNEventAction result = null; + + if ("Reverted".equals(code)) { + result = SVNEventAction.REVERT; + } + else if ("Failed to revert".equals(code)) { + result = SVNEventAction.FAILED_REVERT; + } + else if ("Skipped".equals(code)) { + result = SVNEventAction.SKIP; + } + + return result; + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/RevertClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/RevertClient.java new file mode 100644 index 000000000000..053de373bc0a --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/RevertClient.java @@ -0,0 +1,18 @@ +package org.jetbrains.idea.svn.revert; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface RevertClient extends SvnClient { + + void revert(@NotNull File[] paths, @Nullable SVNDepth depth, @Nullable ISVNEventHandler handler) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/SvnKitRevertClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/SvnKitRevertClient.java new file mode 100644 index 000000000000..17c725cf58f6 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/SvnKitRevertClient.java @@ -0,0 +1,31 @@ +package org.jetbrains.idea.svn.revert; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNWCClient; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitRevertClient extends BaseSvnClient implements RevertClient { + + @Override + public void revert(@NotNull File[] paths, @Nullable SVNDepth depth, @Nullable ISVNEventHandler handler) throws VcsException { + SVNWCClient client = myVcs.createWCClient(); + + client.setEventHandler(handler); + try { + client.doRevert(paths, depth, null); + } + catch (SVNException e) { + throw new VcsException(e); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java index 265d4ca5b9e5..4140ca75a717 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java @@ -28,6 +28,7 @@ import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; +import org.jetbrains.idea.svn.revert.RevertClient; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.*; @@ -74,8 +75,7 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { checker.gather(changes); exceptions.addAll(checker.getExceptions()); - final SVNWCClient client = mySvnVcs.createWCClient(); - client.setEventHandler(new ISVNEventHandler() { + ISVNEventHandler revertHandler = new ISVNEventHandler() { public void handleEvent(SVNEvent event, double progress) { if (event.getAction() == SVNEventAction.REVERT) { final File file = event.getFile(); @@ -91,7 +91,7 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { public void checkCancelled() { listener.checkCanceled(); } - }); + }; final List<CopiedAsideInfo> fromToModified = new ArrayList<CopiedAsideInfo>(); final MultiMap<File, SVNPropertyData> properties = new MultiMap<File, SVNPropertyData>(); @@ -99,7 +99,7 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { // adds (deletes) // deletes (adds) // modifications - final Reverter reverter = new Reverter(client, exceptions); + final Reverter reverter = new Reverter(mySvnVcs.getFactory().createRevertClient(), revertHandler, exceptions); reverter.revert(checker.getForAdds(), true); reverter.revert(checker.getForDeletes(), true); final List<File> edits = checker.getForEdits(); @@ -256,24 +256,36 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { } private static class Reverter { - private final SVNWCClient myClient; + private final RevertClient myClient; + private ISVNEventHandler myHandler; private final List<VcsException> myExceptions; - private Reverter(SVNWCClient client, List<VcsException> exceptions) { + private Reverter(RevertClient client, ISVNEventHandler handler, List<VcsException> exceptions) { myClient = client; + myHandler = handler; myExceptions = exceptions; } public void revert(final File[] files, final boolean recursive) { if (files.length == 0) return; try { - myClient.doRevert(files, recursive ? SVNDepth.INFINITY : SVNDepth.EMPTY, null); + myClient.revert(files, recursive ? SVNDepth.INFINITY : SVNDepth.EMPTY, myHandler); } - catch (SVNException e) { - if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_NOT_DIRECTORY) { - // skip errors on unversioned resources. - myExceptions.add(new VcsException(e)); + catch (VcsException e) { + processRevertError(e); + } + } + + private void processRevertError(@NotNull VcsException e) { + if (e.getCause() instanceof SVNException) { + SVNException cause = (SVNException)e.getCause(); + + // skip errors on unversioned resources. + if (cause.getErrorMessage().getErrorCode() != SVNErrorCode.WC_NOT_DIRECTORY) { + myExceptions.add(e); } + } else { + myExceptions.add(e); } } } @@ -294,18 +306,17 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { } private void revertFileOrDir(File file) throws SVNException, VcsException { - final SVNWCClient wcClient = mySvnVcs.createWCClient(); - SVNInfo info = wcClient.doInfo(file, SVNRevision.UNDEFINED); + SVNInfo info = mySvnVcs.getInfo(file); if (info != null) { if (info.getKind() == SVNNodeKind.FILE) { - wcClient.doRevert(file, false); + doRevert(file, false); } else { if (SVNProperty.SCHEDULE_ADD.equals(info.getSchedule())) { - wcClient.doRevert(file, true); + doRevert(file, true); } else { boolean under17Copy = isUnder17Copy(file, info); if (under17Copy) { - wcClient.doRevert(file, true); + doRevert(file, true); } else { // do update to restore missing directory. mySvnVcs.createUpdateClient().doUpdate(file, SVNRevision.HEAD, true); @@ -317,10 +328,16 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { } } + private void doRevert(@NotNull File path, boolean recursive) throws VcsException { + mySvnVcs.getFactory(path).createRevertClient().revert(new File[]{path}, SVNDepth.fromRecurse(recursive), null); + } + private boolean isUnder17Copy(final File file, final SVNInfo info) throws VcsException { final RootsToWorkingCopies copies = mySvnVcs.getRootsToWorkingCopies(); WorkingCopy copy = copies.getMatchingCopy(info.getURL()); if (copy == null) { + // TODO: Why null could be here? + // TODO: Think we could just rewrite it with mySvnVcs.getWorkingCopyFormat(file) SVNStatus status = null; try { status = mySvnVcs.createStatusClient().doStatus(file, false); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractSvnUpdatePanel.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractSvnUpdatePanel.java index ecc3fa6b5204..d6bdf63cafc8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractSvnUpdatePanel.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractSvnUpdatePanel.java @@ -109,17 +109,8 @@ public abstract class AbstractSvnUpdatePanel { @Nullable private SVNURL getUrlFor(@NotNull final FilePath root) { - try { - SVNWCClient wcClient = myVCS.createWCClient(); - final SVNInfo info = wcClient.doInfo(root.getIOFile(), SVNRevision.UNDEFINED); - if (info != null) { - return info.getURL(); - } - return null; - } - catch (SVNException e) { - return null; - } + final SVNInfo info = myVCS.getInfo(root.getIOFile()); + return info != null ? info.getURL() : null; } protected abstract JComponent getPanel(); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractUpdateIntegrateCrawler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractUpdateIntegrateCrawler.java index e9e7b0b5f897..826f7532b3a0 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractUpdateIntegrateCrawler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractUpdateIntegrateCrawler.java @@ -59,7 +59,7 @@ public abstract class AbstractUpdateIntegrateCrawler implements SvnWCRootCrawler SVNUpdateClient client = myVcs.createUpdateClient(); client.setEventHandler(myHandler); - long rev = doUpdate(root, client); + long rev = doUpdate(root); if (rev < 0 && !isMerge()) { throw new SVNException(SVNErrorMessage.create(SVNErrorCode.UNKNOWN, SvnBundle.message("exception.text.root.was.not.properly.updated", root))); @@ -73,9 +73,7 @@ public abstract class AbstractUpdateIntegrateCrawler implements SvnWCRootCrawler protected abstract void showProgressMessage(ProgressIndicator progress, File root); - protected abstract long doUpdate( - File root, - SVNUpdateClient client) throws SVNException; + protected abstract long doUpdate(File root) throws SVNException; protected abstract boolean isMerge(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/MergeRootInfo.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/MergeRootInfo.java index b8ea0520ebc2..96701b519111 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/MergeRootInfo.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/MergeRootInfo.java @@ -18,6 +18,7 @@ package org.jetbrains.idea.svn.update; import org.jetbrains.idea.svn.SvnVcs; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNWCClient; @@ -33,17 +34,9 @@ public class MergeRootInfo { myRevision1 = SVNRevision.HEAD; myRevision2 = SVNRevision.HEAD; - try { - SVNWCClient wcClient = vcs.createWCClient(); - final SVNURL url = wcClient.doInfo(file, SVNRevision.UNDEFINED).getURL(); - myUrl1 = url.toString(); - myUrl2 = url.toString(); - } - catch (SVNException e) { - myUrl1 = ""; - myUrl2 = ""; - } - + SVNInfo info = vcs.getInfo(file); + myUrl1 = info != null && info.getURL() != null ? info.getURL().toString() : ""; + myUrl2 = myUrl1; } public SVNURL getUrl1() { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnIntegrateEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnIntegrateEnvironment.java index 9e8a5991f41c..9f4a95f7016f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnIntegrateEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnIntegrateEnvironment.java @@ -91,10 +91,7 @@ public class SvnIntegrateEnvironment extends AbstractSvnUpdateIntegrateEnvironme } } - protected long doUpdate( - final File root, - final SVNUpdateClient client) throws - SVNException { + protected long doUpdate(final File root) throws SVNException { final SvnConfiguration svnConfig = SvnConfiguration.getInstance(myVcs.getProject()); MergeRootInfo info = svnConfig.getMergeRootInfo(root, myVcs); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java index 80e43aaf7828..5d2d4153be50 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java @@ -33,7 +33,6 @@ import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNUpdateClient; -import org.tmatesoft.svn.core.wc.SVNWCClient; import java.io.File; import java.util.ArrayList; @@ -76,10 +75,7 @@ public class SvnUpdateEnvironment extends AbstractSvnUpdateIntegrateEnvironment progress.setText(SvnBundle.message("progress.text.updating", root.getAbsolutePath())); } - protected long doUpdate( - final File root, - final SVNUpdateClient client) throws - SVNException { + protected long doUpdate(final File root) throws SVNException { final long rev; final SvnConfiguration configuration = SvnConfiguration.getInstance(myVcs.getProject()); @@ -104,13 +100,15 @@ public class SvnUpdateEnvironment extends AbstractSvnUpdateIntegrateEnvironment private SvnUpdateClientI createUpdateClient(SvnConfiguration configuration, File root, boolean isSwitch, SVNURL sourceUrl) { final SvnUpdateClientI updateClient; + boolean is18Format = myVcs.getWorkingCopyFormat(root) == WorkingCopyFormat.ONE_DOT_EIGHT; + // do not do from command line for switch now - if (! isSwitch && SvnConfiguration.UseAcceleration.commandLine.equals(configuration.myUseAcceleration) && + if (! isSwitch && (is18Format || SvnConfiguration.UseAcceleration.commandLine.equals(configuration.myUseAcceleration) && Svn17Detector.is17(myVcs.getProject(), root) && ( SvnAuthenticationManager.HTTP.equals(sourceUrl.getProtocol()) || SvnAuthenticationManager.HTTPS.equals(sourceUrl.getProtocol()) - )) { - updateClient = new SvnCommandLineUpdateClient(myVcs.getProject(), null); + ))) { + updateClient = new SvnCommandLineUpdateClient(myVcs, null); } else { updateClient = new SvnSvnkitUpdateClient(myVcs.createUpdateClient()); } @@ -129,18 +127,8 @@ public class SvnUpdateEnvironment extends AbstractSvnUpdateIntegrateEnvironment @Nullable private static SVNURL getSourceUrl(final SvnVcs vcs, final File root) { - try { - SVNWCClient wcClient = vcs.createWCClient(); - final SVNInfo svnInfo = wcClient.doInfo(root, SVNRevision.UNDEFINED); - if (svnInfo != null) { - return svnInfo.getURL(); - } else { - return null; - } - } - catch (SVNException e) { - return null; - } + final SVNInfo svnInfo = vcs.getInfo(root); + return svnInfo != null ? svnInfo.getURL() : null; } public boolean validateOptions(final Collection<FilePath> roots) { @@ -193,9 +181,8 @@ public class SvnUpdateEnvironment extends AbstractSvnUpdateIntegrateEnvironment // false - do not do update private boolean checkAncestry(final File sourceFile, final SVNURL targetUrl, final SVNRevision targetRevision) throws SVNException { - final SVNWCClient client = myVcs.createWCClient(); - final SVNInfo sourceSvnInfo = client.doInfo(sourceFile, SVNRevision.UNDEFINED); - final SVNInfo targetSvnInfo = client.doInfo(targetUrl, SVNRevision.UNDEFINED, targetRevision); + final SVNInfo sourceSvnInfo = myVcs.getInfo(sourceFile); + final SVNInfo targetSvnInfo = myVcs.getInfo(targetUrl, targetRevision); if (sourceSvnInfo == null || targetSvnInfo == null) { // cannot check diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/UpdateRootInfo.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/UpdateRootInfo.java index 4852e9f6b95f..201260e232f2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/UpdateRootInfo.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/UpdateRootInfo.java @@ -31,20 +31,9 @@ public class UpdateRootInfo { public UpdateRootInfo(File file, SvnVcs vcs) { myRevision = SVNRevision.HEAD; - try { - SVNWCClient wcClient = vcs.createWCClient(); - SVNInfo info = wcClient.doInfo(file, SVNRevision.UNDEFINED); - if (info != null) { - final SVNURL url = info.getURL(); - myUrl = url.toString(); - } else { - myUrl = ""; - } - } - catch (SVNException e) { - myUrl = ""; - } + SVNInfo info = vcs.getInfo(file); + myUrl = info != null && info.getURL() != null ? info.getDepth().toString() : ""; } public SVNURL getUrl() { diff --git a/plugins/svn4idea/svn4idea-tests.iml b/plugins/svn4idea/svn4idea-tests.iml index 9a537c253abf..0b826ed87489 100644 --- a/plugins/svn4idea/svn4idea-tests.iml +++ b/plugins/svn4idea/svn4idea-tests.iml @@ -38,7 +38,6 @@ </SOURCES> </library> </orderEntry> - <orderEntry type="module" module-name="bindSvn" /> </component> </module> diff --git a/plugins/svn4idea/svn4idea.iml b/plugins/svn4idea/svn4idea.iml index 4f0d931cd13f..53dd66166d64 100644 --- a/plugins/svn4idea/svn4idea.iml +++ b/plugins/svn4idea/svn4idea.iml @@ -76,16 +76,6 @@ </SOURCES> </library> </orderEntry> - <orderEntry type="module" module-name="bindSvn" /> - <orderEntry type="module-library"> - <library> - <CLASSES> - <root url="jar://$MODULE_DIR$/bindSvn/lib/javahl.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> </component> </module> diff --git a/plugins/svn4idea/testData18/svn/bin/windows/svn.exe b/plugins/svn4idea/testData18/svn/bin/windows/svn.exe Binary files differnew file mode 100644 index 000000000000..827753f442aa --- /dev/null +++ b/plugins/svn4idea/testData18/svn/bin/windows/svn.exe diff --git a/plugins/svn4idea/testData18/svn/newrepo.zip b/plugins/svn4idea/testData18/svn/newrepo.zip Binary files differnew file mode 100644 index 000000000000..fd9a577a39a0 --- /dev/null +++ b/plugins/svn4idea/testData18/svn/newrepo.zip diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/SvnTestCase.java b/plugins/svn4idea/testSource/org/jetbrains/idea/SvnTestCase.java index 14d1b4c42897..d5a55520adae 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/SvnTestCase.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/SvnTestCase.java @@ -78,13 +78,19 @@ import static org.junit.Assert.assertTrue; * @author yole */ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { + + public static String ourGlobalTestDataDir; + public static Boolean ourGlobalUseNativeAcceleration; + protected TempDirTestFixture myTempDirFixture; protected String myRepoUrl; protected TestClientRunner myRunner; protected String myWcRootName; - protected boolean myUseNativeAcceleration = new GregorianCalendar().get(Calendar.HOUR_OF_DAY) % 2 == 0; + // TODO: Change this to explicitly run either with native acceleration or not. + // properties set through run configurations or different runners (like Suite) could be used + private boolean myUseNativeAcceleration = new GregorianCalendar().get(Calendar.HOUR_OF_DAY) % 2 == 0; - protected final String myTestDataDir; + private String myTestDataDir; private File myRepoRoot; private File myWcRoot; private ChangeListManagerGate myGate; @@ -118,7 +124,13 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { @Before public void setUp() throws Exception { - System.out.println("Native client for status: " + myUseNativeAcceleration); + System.out.println("Native client for status: " + isUseNativeAcceleration()); + + String property = System.getProperty("svn.test.data.directory"); + if (!StringUtil.isEmpty(property)) { + myTestDataDir = property; + } + UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override public void run() { @@ -138,7 +150,7 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { myPluginRoot = new File(rootPath).getParentFile().getParentFile().getParentFile(); } - File svnBinDir = new File(myPluginRoot, myTestDataDir + "/svn/bin"); + File svnBinDir = new File(myPluginRoot, getTestDataDir() + "/svn/bin"); File svnExecutable = null; if (SystemInfo.isWindows) { svnExecutable = new File(svnBinDir, "windows/svn.exe"); @@ -156,7 +168,7 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { ? createClientRunner(Collections.singletonMap("DYLD_LIBRARY_PATH", myClientBinaryPath.getPath())) : createClientRunner(); - ZipUtil.extract(new File(myPluginRoot, myTestDataDir + "/svn/newrepo.zip"), myRepoRoot, null); + ZipUtil.extract(new File(myPluginRoot, getTestDataDir() + "/svn/newrepo.zip"), myRepoRoot, null); myWcRoot = new File(myTempDirFixture.getTempDirPath(), myWcRootName); assert myWcRoot.mkdir() || myWcRoot.isDirectory() : myWcRoot; @@ -205,7 +217,7 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { @Override protected void projectCreated() { - if (myUseNativeAcceleration) { + if (isUseNativeAcceleration()) { SvnConfiguration.getInstance(myProject).myUseAcceleration = SvnConfiguration.UseAcceleration.commandLine; SvnApplicationSettings.getInstance().setCommandLinePath(myClientBinaryPath + File.separator + "svn"); } @@ -319,6 +331,22 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { //clManager.ensureUpToDate(false); } + public String getTestDataDir() { + return StringUtil.isEmpty(ourGlobalTestDataDir) ? myTestDataDir : ourGlobalTestDataDir; + } + + public void setTestDataDir(String testDataDir) { + myTestDataDir = testDataDir; + } + + public boolean isUseNativeAcceleration() { + return ourGlobalUseNativeAcceleration != null ? ourGlobalUseNativeAcceleration : myUseNativeAcceleration; + } + + public void setUseNativeAcceleration(boolean useNativeAcceleration) { + myUseNativeAcceleration = useNativeAcceleration; + } + protected class SubTree { public VirtualFile myRootDir; public VirtualFile mySourceDir; diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java index 270e6d49c607..bded7affdf2c 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java @@ -54,6 +54,7 @@ public class IgnoredFilesTest extends Svn17TestCase { @Before public void setUp() throws Exception { + super.setUp(); UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override public void run() { diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedNativeTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedNativeTest.java index a9c1074a3d94..6153d658767b 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedNativeTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedNativeTest.java @@ -34,7 +34,7 @@ public class SvnChangesCorrectlyRefreshedNativeTest extends Svn17TestCase { @Override public void setUp() throws Exception { - myUseNativeAcceleration = true; + setUseNativeAcceleration(true); super.setUp(); clManager = ChangeListManager.getInstance(myProject); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommitTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommitTest.java index f4da04ea201a..f18b6a578ecb 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommitTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommitTest.java @@ -465,7 +465,7 @@ public class SvnCommitTest extends Svn17TestCase { return feedback; } - private void checkinFile(VirtualFile file, FileStatus status) { + protected void checkinFile(VirtualFile file, FileStatus status) { final Change change = myChangeListManager.getChange(file); Assert.assertNotNull(change); Assert.assertEquals(status, change.getFileStatus()); @@ -478,6 +478,7 @@ public class SvnCommitTest extends Svn17TestCase { } protected void run2variants(final MyRunner runner) throws Exception { + // TODO: Change this to run different variants separately. See SvnTestCase.myUseAcceleration. setNativeAcceleration(false); runner.run(); runner.cleanup(); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnExternalTests.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnExternalTests.java index 500acbdff4c9..3e3b2fda1665 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnExternalTests.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnExternalTests.java @@ -27,6 +27,7 @@ import com.intellij.openapi.vcs.changes.ChangeListManagerImpl; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ui.UIUtil; import junit.framework.Assert; import org.junit.Test; @@ -171,8 +172,7 @@ public class SvnExternalTests extends Svn17TestCase { private void updatedCreatedExternalFromIDEAImpl() { final File sourceDir = new File(myWorkingCopyDir.getPath(), "source"); - ProjectLevelVcsManager.getInstance(myProject).setDirectoryMappings( - Arrays.asList(new VcsDirectoryMapping(FileUtil.toSystemIndependentName(sourceDir.getPath()), myVcs.getName()))); + setNewDirectoryMappings(sourceDir); imitUpdate(myProject); final File externalFile = new File(sourceDir, "external/t11.txt"); @@ -180,6 +180,16 @@ public class SvnExternalTests extends Svn17TestCase { Assert.assertNotNull(externalVf); } + private void setNewDirectoryMappings(final File sourceDir) { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + ProjectLevelVcsManager.getInstance(myProject).setDirectoryMappings( + Arrays.asList(new VcsDirectoryMapping(FileUtil.toSystemIndependentName(sourceDir.getPath()), myVcs.getName()))); + } + }); + } + @Test public void testUncommittedExternalStatus() throws Exception { prepareExternal(false, true, false); @@ -221,8 +231,7 @@ public class SvnExternalTests extends Svn17TestCase { private void uncommittedExternalCopyIsDetectedImpl() { final File sourceDir = new File(myWorkingCopyDir.getPath(), "source"); - ProjectLevelVcsManager.getInstance(myProject).setDirectoryMappings( - Arrays.asList(new VcsDirectoryMapping(FileUtil.toSystemIndependentName(sourceDir.getPath()), myVcs.getName()))); + setNewDirectoryMappings(sourceDir); imitUpdate(myProject); refreshSvnMappingsSynchronously(); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnLockingTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnLockingTest.java index 171bb1c35cb8..7c32af921f56 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnLockingTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnLockingTest.java @@ -22,6 +22,7 @@ import com.intellij.util.concurrency.Semaphore; import junit.framework.Assert; import junit.framework.TestCase; import org.junit.Before; +import org.junit.Ignore; import org.tmatesoft.sqljet.core.SqlJetException; import org.tmatesoft.sqljet.core.table.ISqlJetBusyHandler; import org.tmatesoft.sqljet.core.table.ISqlJetTransaction; @@ -42,6 +43,8 @@ import java.util.concurrent.atomic.AtomicBoolean; * Date: 10/23/12 * Time: 2:27 PM */ +// TODO: Locking functionality which is tested by this test is not required anymore. Likely test needs to be removed. +@Ignore public class SvnLockingTest extends TestCase { private File myWorkingCopyRoot; private SvnTestWriteOperationLocks myLocks; diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java index 61aca5670867..b6d6ae104bbc 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java @@ -72,7 +72,7 @@ public class SvnNativeClientAuthTest extends Svn17TestCase { @Before public void setUp() throws Exception { super.setUp(); - final File certFile = new File(myPluginRoot, myTestDataDir + "/svn/____.pfx"); + final File certFile = new File(myPluginRoot, getTestDataDir() + "/svn/____.pfx"); setNativeAcceleration(true); myVcs = SvnVcs.getInstance(myProject); // replace authentication provider so that pass credentials without dialogs diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnChangesCorrectlyRefreshedNativeTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnChangesCorrectlyRefreshedNativeTest.java index 205733657075..8939f1cbf164 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnChangesCorrectlyRefreshedNativeTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnChangesCorrectlyRefreshedNativeTest.java @@ -49,7 +49,7 @@ public class SvnChangesCorrectlyRefreshedNativeTest extends Svn17TestCase { @Override public void setUp() throws Exception { - myUseNativeAcceleration = true; + setUseNativeAcceleration(true); super.setUp(); clManager = ChangeListManager.getInstance(myProject); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn18/Svn18TestSuite.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn18/Svn18TestSuite.java new file mode 100644 index 000000000000..a7019738b9b5 --- /dev/null +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn18/Svn18TestSuite.java @@ -0,0 +1,33 @@ +package org.jetbrains.idea.svn18; + +import org.jetbrains.idea.SvnTestCase; +import org.jetbrains.idea.svn.*; +import org.junit.ClassRule; +import org.junit.rules.ExternalResource; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +/** + * @author Konstantin Kolosovsky. + */ +// TODO: Add svn clients for mac and linux +@RunWith(Suite.class) +@Suite.SuiteClasses({SvnAddTest.class, SvnDeleteTest.class}) +public class Svn18TestSuite { + + @ClassRule + public static TestRule configuration = new ExternalResource() { + @Override + protected void before() throws Throwable { + SvnTestCase.ourGlobalTestDataDir = "testData18"; + SvnTestCase.ourGlobalUseNativeAcceleration = true; + } + + @Override + protected void after() { + SvnTestCase.ourGlobalTestDataDir = null; + SvnTestCase.ourGlobalUseNativeAcceleration = null; + } + }; +} diff --git a/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepository.java b/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepository.java index 13b8e7df6823..ffa911826be8 100644 --- a/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepository.java +++ b/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepository.java @@ -46,14 +46,12 @@ public abstract class TaskRepository { public static final int STATE_UPDATING = 0x0008; /** * Supporting this feature means that server implements some kind of issues filtering. - * It may be special query language like the one used in Youtrack or mere plain + * It may be special query language like the one used in YouTrack or mere plain * text search. - * If server supports this feature it MUST return tasks already - * filtered according to {@code query} parameter from {@code getIssues} method. - * Otherwise they will be filtered using {@code TaskSearchSupport#filterTasks} - * - * @see com.intellij.tasks.impl.TaskManagerImpl - * @see com.intellij.tasks.actions.TaskSearchSupport + * <p> + * If server supports this feature it MUST return tasks already filtered according + * to {@code query} parameter from {@link #getIssues}} method, otherwise they will + * be filtered internally in {@link TaskManager#getIssues} */ public static final int NATIVE_SEARCH = 0x0010; @@ -106,7 +104,7 @@ public abstract class TaskRepository { /** * Get issues from the repository. If query is null, return issues should assigned to current user only. - * If server supports {@code NATIVE_SEARCH} feature, tasks returned MUST be filtered by specified query. + * If server supports {@link #NATIVE_SEARCH} feature, tasks returned MUST be filtered by specified query. * * @param query repository specific. * @param max maximum issues number to return diff --git a/plugins/tasks/tasks-core/src/META-INF/plugin.xml b/plugins/tasks/tasks-core/src/META-INF/plugin.xml index 3573bb180bb9..dd42d52a770e 100644 --- a/plugins/tasks/tasks-core/src/META-INF/plugin.xml +++ b/plugins/tasks/tasks-core/src/META-INF/plugin.xml @@ -8,6 +8,8 @@ <!--fake dependency for Web IDE--> <depends>com.intellij.modules.xml</depends> <depends optional="true" config-file="java-contexts.xml">com.intellij.modules.java</depends> + <!-- Optional dependency on XPath plugin for syntax highlighting in GenericRepository configuration dialog --> + <depends optional="true">XPathView</depends> <project-components> <component> diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/GotoTaskAction.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/GotoTaskAction.java index cdc20c5e5625..756abdeb526e 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/GotoTaskAction.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/GotoTaskAction.java @@ -14,7 +14,6 @@ import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.util.Ref; -import com.intellij.psi.PsiManager; import com.intellij.tasks.LocalTask; import com.intellij.tasks.Task; import com.intellij.tasks.TaskManager; @@ -22,7 +21,6 @@ import com.intellij.tasks.doc.TaskPsiElement; import com.intellij.tasks.impl.TaskManagerImpl; import com.intellij.tasks.impl.TaskUtil; import com.intellij.util.ArrayUtil; -import com.intellij.util.Function; import com.intellij.util.IconUtil; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; @@ -67,53 +65,25 @@ public class GotoTaskAction extends GotoActionBase implements DumbAware { boolean everywhere, @NotNull ProgressIndicator cancelled, @NotNull Processor<Object> consumer) { - List<Task> cachedAndLocalTasks = TaskSearchSupport.getLocalAndCachedTasks(TaskManager.getManager(project), pattern, everywhere); - List<TaskPsiElement> taskPsiElements = ContainerUtil.map(cachedAndLocalTasks, new Function<Task, TaskPsiElement>() { - @Override - public TaskPsiElement fun(Task task) { - return new TaskPsiElement(PsiManager.getInstance(project), task); - } - }); CREATE_NEW_TASK_ACTION.setTaskName(pattern); - cancelled.checkCanceled(); if (!consumer.process(CREATE_NEW_TASK_ACTION)) return false; - boolean cachedTasksFound = taskPsiElements.size() != 0; - if (cachedTasksFound) { - cancelled.checkCanceled(); - if (!consumer.process(ChooseByNameBase.NON_PREFIX_SEPARATOR)) return false; - } - - for (Object element : taskPsiElements) { - cancelled.checkCanceled(); - if (!consumer.process(element)) return false; - } + List<Task> cachedAndLocalTasks = TaskSearchSupport.getLocalAndCachedTasks(TaskManager.getManager(project), pattern, everywhere); + boolean cachedTasksFound = !cachedAndLocalTasks.isEmpty(); + if (!processTasks(cachedAndLocalTasks, consumer, cachedTasksFound, cancelled)) return false; List<Task> tasks = TaskSearchSupport .getRepositoriesTasks(TaskManager.getManager(project), pattern, base.getMaximumListSizeLimit(), 0, true, everywhere, cancelled); tasks.removeAll(cachedAndLocalTasks); - taskPsiElements = ContainerUtil.map(tasks, new Function<Task, TaskPsiElement>() { - @Override - public TaskPsiElement fun(Task task) { - return new TaskPsiElement(PsiManager.getInstance(project), task); - } - }); - if (!cachedTasksFound && taskPsiElements.size() != 0) { - cancelled.checkCanceled(); - if (!consumer.process(ChooseByNameBase.NON_PREFIX_SEPARATOR)) return false; - } - - for (Object element : taskPsiElements) { - cancelled.checkCanceled(); - if (!consumer.process(element)) return false; - } - return true; + return processTasks(tasks, consumer, cachedTasksFound, cancelled); } }, null, false, 0); + popup.setShowListForEmptyPattern(true); popup.setSearchInAnyPlace(true); + popup.setFixLostTyping(false); popup.setAdText("<html>Press SHIFT to merge with current context<br/>" + "Pressing " + KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction(IdeActions.ACTION_QUICK_JAVADOC)) + @@ -166,6 +136,16 @@ public class GotoTaskAction extends GotoActionBase implements DumbAware { }, null, popup); } + private static boolean processTasks(List<Task> tasks, Processor<Object> consumer, boolean cachedTasksFound, ProgressIndicator cancelled) { + if (!cachedTasksFound && !tasks.isEmpty() && !consumer.process(ChooseByNameBase.NON_PREFIX_SEPARATOR)) return false; + + for (Object element : tasks) { + cancelled.checkCanceled(); + if (!consumer.process(element)) return false; + } + return true; + } + private static void showOpenTaskDialog(final Project project, final Task task) { JBPopup hint = DocumentationManager.getInstance(project).getDocInfoHint(); if (hint != null) hint.cancel(); diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/OpenTaskDialog.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/OpenTaskDialog.java index 489afb921af2..3eeea41976ec 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/OpenTaskDialog.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/OpenTaskDialog.java @@ -117,6 +117,11 @@ public class OpenTaskDialog extends DialogWrapper { @Override protected void doOKAction() { + createTask(); + super.doOKAction(); + } + + public void createTask() { TaskManagerImpl taskManager = (TaskManagerImpl)TaskManager.getManager(myProject); taskManager.getState().markAsInProgress = isMarkAsInProgress(); @@ -133,18 +138,17 @@ public class OpenTaskDialog extends DialogWrapper { LOG.warn(ex); } } + LocalTask activeTask = taskManager.getActiveTask(); LocalTask localTask = taskManager.activateTask(myTask, isClearContext()); if (myCreateChangelist.isSelected()) { taskManager.createChangeList(localTask, myChangelistName.getText()); } if (myCreateBranch.isSelected()) { - LocalTask activeTask = taskManager.getActiveTask(); taskManager.createBranch(localTask, activeTask, myBranchName.getText()); } if (myTask.getType() == TaskType.EXCEPTION && AnalyzeTaskStacktraceAction.hasTexts(myTask)) { AnalyzeTaskStacktraceAction.analyzeStacktrace(myTask, myProject); } - super.doOKAction(); } @Nullable diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskCellRenderer.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskCellRenderer.java index 65a4d454e864..bb8f458d7f0e 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskCellRenderer.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskCellRenderer.java @@ -7,7 +7,6 @@ import com.intellij.openapi.util.IconLoader; import com.intellij.tasks.LocalTask; import com.intellij.tasks.Task; import com.intellij.tasks.TaskManager; -import com.intellij.tasks.doc.TaskPsiElement; import com.intellij.ui.LayeredIcon; import com.intellij.ui.SimpleColoredComponent; import com.intellij.ui.SimpleTextAttributes; @@ -39,8 +38,8 @@ public class TaskCellRenderer extends DefaultListCellRenderer implements Matcher final JPanel panel = new JPanel(new BorderLayout()); panel.setBackground(UIUtil.getListBackground(sel)); panel.setForeground(UIUtil.getListForeground(sel)); - if (value instanceof TaskPsiElement) { - final Task task = ((TaskPsiElement)value).getTask(); + if (value instanceof Task) { + final Task task = ((Task)value); final SimpleColoredComponent c = new SimpleColoredComponent(); final TaskManager taskManager = TaskManager.getManager(myProject); final boolean isLocalTask = taskManager.findTask(task.getId()) != null; diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskSearchSupport.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskSearchSupport.java index c2c6306207e2..98704b7c1429 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskSearchSupport.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskSearchSupport.java @@ -77,12 +77,7 @@ public class TaskSearchSupport { String pattern, boolean cached, boolean autopopup) { - final Matcher matcher = getMatcher(pattern); - return ContainerUtil.mapNotNull(getTasks(pattern, cached, autopopup, myManager), new NullableFunction<Task, Task>() { - public Task fun(Task task) { - return matcher.matches(task.getId()) || matcher.matches(task.getSummary()) ? task : null; - } - }); + return filterTasks(pattern, getTasks(pattern, cached, autopopup, myManager)); } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/context/WorkingContextManager.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/context/WorkingContextManager.java index 5c4733b4d4ca..f437897dcb7b 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/context/WorkingContextManager.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/context/WorkingContextManager.java @@ -27,6 +27,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.tasks.Task; import com.intellij.util.NullableFunction; @@ -170,7 +171,7 @@ public class WorkingContextManager { //noinspection ResultOfMethodCallIgnored tasksFolder.mkdir(); } - String projectName = myProject.getName(); + String projectName = FileUtil.sanitizeFileName(myProject.getName()); return new File(tasksFolder, projectName + postfix); } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepository.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepository.java index f3ed0f92b411..c4c5869fbab4 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepository.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepository.java @@ -16,6 +16,7 @@ import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.GetMethod; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -31,19 +32,22 @@ import static com.intellij.tasks.generic.TemplateVariable.*; public class GenericRepository extends BaseRepositoryImpl { private static final Logger LOG = Logger.getInstance(GenericRepository.class); - public final PredefinedFactoryVariable SERVER_URL_TEMPLATE_VARIABLE = new PredefinedFactoryVariable("serverUrl") { + public final FactoryVariable SERVER_URL_TEMPLATE_VARIABLE = new FactoryVariable("serverUrl") { + @NotNull @Override public String getValue() { return GenericRepository.this.getUrl(); } }; - public final PredefinedFactoryVariable USERNAME_TEMPLATE_VARIABLE = new PredefinedFactoryVariable("username") { + public final FactoryVariable USERNAME_TEMPLATE_VARIABLE = new FactoryVariable("username") { + @NotNull @Override public String getValue() { return GenericRepository.this.getUsername(); } }; - public final PredefinedFactoryVariable PASSWORD_TEMPLATE_VARIABLE = new PredefinedFactoryVariable("password", true) { + public final FactoryVariable PASSWORD_TEMPLATE_VARIABLE = new FactoryVariable("password", true) { + @NotNull @Override public String getValue() { return GenericRepository.this.getPassword(); @@ -155,7 +159,7 @@ public class GenericRepository extends BaseRepositoryImpl { public boolean isConfigured() { if (!super.isConfigured()) return false; for (TemplateVariable variable : getTemplateVariables()) { - if (variable.getIsShownOnFirstTab() && StringUtil.isEmpty(variable.getValue())) { + if (variable.isShownOnFirstTab() && StringUtil.isEmpty(variable.getValue())) { return false; } } @@ -171,8 +175,8 @@ public class GenericRepository extends BaseRepositoryImpl { executeMethod(getLoginMethod()); } List<TemplateVariable> variables = concat(getAllTemplateVariables(), - new TemplateVariable("max", max), - new TemplateVariable("since", since)); + new TemplateVariable("max", String.valueOf(max)), + new TemplateVariable("since", String.valueOf(since))); String requestUrl = GenericRepositoryUtil.substituteTemplateVariables(getTasksListUrl(), variables); String responseBody = executeMethod(getHttpMethod(requestUrl, myTasksListMethodType)); Task[] tasks = getActiveResponseHandler().parseIssues(responseBody, max); diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepositoryEditor.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepositoryEditor.java index 6ddb8da103b5..c677744fe958 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepositoryEditor.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepositoryEditor.java @@ -131,7 +131,7 @@ public class GenericRepositoryEditor<T extends GenericRepository> extends BaseRe myRepository.setTemplateVariables(ContainerUtil.filter(dialog.getTemplateVariables(), new Condition<TemplateVariable>() { @Override public boolean value(TemplateVariable variable) { - return !variable.getIsPredefined(); + return !variable.isReadOnly(); } })); myCustomPanel.removeAll(); @@ -186,8 +186,8 @@ public class GenericRepositoryEditor<T extends GenericRepository> extends BaseRe myField2Variable = new IdentityHashMap<JTextField, TemplateVariable>(); FormBuilder builder = FormBuilder.createFormBuilder(); for (final TemplateVariable variable : myRepository.getTemplateVariables()) { - if (variable.getIsShownOnFirstTab()) { - JTextField field = variable.getIsHidden() ? new JPasswordField(variable.getValue()) : new JTextField(variable.getValue()); + if (variable.isShownOnFirstTab()) { + JTextField field = variable.isHidden() ? new JPasswordField(variable.getValue()) : new JTextField(variable.getValue()); myField2Variable.put(field, variable); installListener(field); JBLabel label = new JBLabel(prettifyVariableName(variable.getName()) + ":", SwingConstants.RIGHT); diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepositoryType.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepositoryType.java index 1a9bb42ca461..95f865b846c1 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepositoryType.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepositoryType.java @@ -59,7 +59,8 @@ public class GenericRepositoryType extends BaseRepositoryType<GenericRepository> public List<TaskRepositorySubtype> getAvailableSubtypes() { return Arrays.asList( this, - new AsanaRepository() + new AsanaRepository(), + new AssemblaRepository() ); } @@ -111,4 +112,10 @@ public class GenericRepositoryType extends BaseRepositoryType<GenericRepository> super("Asana", TasksIcons.Asana); } } + + public class AssemblaRepository extends GenericSubtype { + public AssemblaRepository() { + super("Assembla", TasksIcons.Assembla); + } + } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/JsonPathResponseHandler.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/JsonPathResponseHandler.java index cc80ed29e5ba..c97a77fcf6fd 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/JsonPathResponseHandler.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/JsonPathResponseHandler.java @@ -1,7 +1,5 @@ package com.intellij.tasks.generic; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.fileTypes.PlainTextFileType; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Function; @@ -17,7 +15,7 @@ import java.util.List; import java.util.Map; /** - * Author: Mikhail Golubev + * @author Mikhail Golubev */ @Tag("JsonResponseHandler") public final class JsonPathResponseHandler extends SelectorBasedResponseHandler { @@ -44,11 +42,6 @@ public final class JsonPathResponseHandler extends SelectorBasedResponseHandler super(repository); } - @Override - public FileType getSelectorFileType() { - return PlainTextFileType.INSTANCE; - } - @Nullable private Object extractRawValue(@NotNull Selector selector, @NotNull String source) throws Exception { if (StringUtil.isEmpty(selector.getPath())) { @@ -140,6 +133,7 @@ public final class JsonPathResponseHandler extends SelectorBasedResponseHandler return myCompiledCache.get(path); } + @NotNull @Override public ResponseType getResponseType() { return ResponseType.JSON; diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/ManageTemplateVariablesDialog.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/ManageTemplateVariablesDialog.java index e9e6d5e8460f..3c3ed235454a 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/ManageTemplateVariablesDialog.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/ManageTemplateVariablesDialog.java @@ -60,7 +60,7 @@ public class ManageTemplateVariablesDialog extends DialogWrapper { @Override public boolean isCellEditable(TemplateVariable templateVariable) { - return !templateVariable.getIsPredefined(); + return !templateVariable.isReadOnly(); } @Override @@ -82,7 +82,7 @@ public class ManageTemplateVariablesDialog extends DialogWrapper { @Override public boolean isCellEditable(TemplateVariable templateVariable) { - return !templateVariable.getIsPredefined(); + return !templateVariable.isReadOnly(); } @Override @@ -93,7 +93,7 @@ public class ManageTemplateVariablesDialog extends DialogWrapper { @Override public TableCellRenderer getRenderer(TemplateVariable variable) { - if (variable.getIsHidden()) { + if (variable.isHidden()) { return new TableCellRenderer() { @Override public Component getTableCellRendererComponent(JTable table, @@ -112,7 +112,7 @@ public class ManageTemplateVariablesDialog extends DialogWrapper { @Nullable @Override public TableCellEditor getEditor(final TemplateVariable variable) { - if (variable.getIsHidden()) { + if (variable.isHidden()) { return new AbstractTableCellEditor() { private JPasswordField myPasswordField; @Override @@ -141,12 +141,12 @@ public class ManageTemplateVariablesDialog extends DialogWrapper { @Nullable @Override public Boolean valueOf(TemplateVariable o) { - return o.getIsShownOnFirstTab(); + return o.isShownOnFirstTab(); } @Override public void setValue(TemplateVariable variable, Boolean value) { - variable.setIsShownOnFirstTab(value); + variable.setShownOnFirstTab(value); setModified(); } @@ -157,7 +157,7 @@ public class ManageTemplateVariablesDialog extends DialogWrapper { @Override public boolean isCellEditable(TemplateVariable variable) { - return !variable.getIsPredefined(); + return !variable.isReadOnly(); } @Nullable @@ -171,12 +171,12 @@ public class ManageTemplateVariablesDialog extends DialogWrapper { @Nullable @Override public Boolean valueOf(TemplateVariable o) { - return o.getIsHidden(); + return o.isHidden(); } @Override public void setValue(TemplateVariable variable, Boolean value) { - variable.setIsHidden(value); + variable.setHidden(value); setModified(); // value column editor may be changed TemplateVariablesTable.this.refreshValues(); @@ -189,7 +189,7 @@ public class ManageTemplateVariablesDialog extends DialogWrapper { @Override public boolean isCellEditable(TemplateVariable variable) { - return !variable.getIsPredefined(); + return !variable.isReadOnly(); } @Nullable @@ -203,7 +203,7 @@ public class ManageTemplateVariablesDialog extends DialogWrapper { @Override protected TemplateVariable createElement() { - return new TemplateVariable("", "", false, null); + return new TemplateVariable("", ""); } @Override diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/RegExResponseHandler.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/RegExResponseHandler.java index c08085194712..4d7f5a50dab2 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/RegExResponseHandler.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/RegExResponseHandler.java @@ -68,8 +68,9 @@ public final class RegExResponseHandler extends ResponseHandler { return myTaskRegex.hashCode(); } + @NotNull @Override - public JComponent getConfigurationComponent(Project project) { + public JComponent getConfigurationComponent(@NotNull Project project) { FormBuilder builder = FormBuilder.createFormBuilder(); final EditorTextField taskPatternText; taskPatternText = new LanguageTextField(RegExpLanguage.INSTANCE, project, myTaskRegex, false); @@ -86,7 +87,7 @@ public final class RegExResponseHandler extends ResponseHandler { @NotNull @Override - public Task[] parseIssues(String response, int max) throws Exception { + public Task[] parseIssues(@NotNull String response, int max) throws Exception { final List<String> placeholders = getPlaceholders(myTaskRegex); if (!placeholders.contains(ID_PLACEHOLDER) || !placeholders.contains(SUMMARY_PLACEHOLDER)) { throw new Exception("Incorrect Task Pattern"); @@ -120,7 +121,7 @@ public final class RegExResponseHandler extends ResponseHandler { @Nullable @Override - public Task parseIssue(String response) throws Exception { + public Task parseIssue(@NotNull String response) throws Exception { return null; } @@ -150,6 +151,7 @@ public final class RegExResponseHandler extends ResponseHandler { return !StringUtil.isEmpty(myTaskRegex); } + @NotNull @Override public ResponseType getResponseType() { return ResponseType.TEXT; diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/ResponseHandler.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/ResponseHandler.java index d51c38ac1bf7..cb8830b6f7cf 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/ResponseHandler.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/ResponseHandler.java @@ -10,7 +10,7 @@ import javax.swing.*; /** * ResponseHandler subclasses represent different strategies of extracting tasks from - * task server responses (e.g. using regular expressions, XPath, JSONPath, CSS selector etc.). + * task server responses (e.g. using regular expressions, XPath, JSONPath, CSS selector etc.) * * @see XPathResponseHandler * @see JsonPathResponseHandler @@ -19,36 +19,40 @@ import javax.swing.*; */ public abstract class ResponseHandler implements Cloneable { - // XXX: what about serialization of circular dependencies? protected GenericRepository myRepository; - // Serialization constructor + /** + * Serialization constructor + */ public ResponseHandler() { // empty } - public ResponseHandler(GenericRepository repository) { + public ResponseHandler(@NotNull GenericRepository repository) { myRepository = repository; } - public void setRepository(GenericRepository repository) { + public void setRepository(@NotNull GenericRepository repository) { myRepository = repository; } + @NotNull @Transient public GenericRepository getRepository() { return myRepository; } - public abstract JComponent getConfigurationComponent(Project project); + @NotNull + public abstract JComponent getConfigurationComponent(@NotNull Project project); + @NotNull public abstract ResponseType getResponseType(); @NotNull - public abstract Task[] parseIssues(String response, int max) throws Exception; + public abstract Task[] parseIssues(@NotNull String response, int max) throws Exception; @Nullable - public abstract Task parseIssue(String response) throws Exception; + public abstract Task parseIssue(@NotNull String response) throws Exception; public abstract boolean isConfigured(); diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/ResponseType.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/ResponseType.java index aa4bd782b35c..8889e26babfc 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/ResponseType.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/ResponseType.java @@ -2,40 +2,82 @@ package com.intellij.tasks.generic; import com.intellij.ide.highlighter.HtmlFileType; import com.intellij.ide.highlighter.XmlFileType; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.fileTypes.PlainTextFileType; import org.intellij.lang.regexp.RegExpFileType; -import org.intellij.lang.xpath.XPathFileType; +import org.jetbrains.annotations.NotNull; + /** - * User: evgeny.zakrevsky - * Date: 10/25/12 + * ResponseType contains information about what selector types used + * to extract information from server responses with specific content-type. + * + * @author evgeny.zakrevsky + * @author Mikhail Golubev */ public enum ResponseType { - XML("application/xml", XmlFileType.INSTANCE, XPathFileType.XPATH2), - JSON("application/json", PlainTextFileType.INSTANCE, PlainTextFileType.INSTANCE), + + XML("application/xml", XmlFileType.INSTANCE, findXPathFileType()), + JSON("application/json", findFileTypePlainDefault("JSON"), PlainTextFileType.INSTANCE), // TODO: think about possible selector type if it needed at all (e.g. CSS selector) HTML("text/html", HtmlFileType.INSTANCE, PlainTextFileType.INSTANCE), TEXT("text/plain", PlainTextFileType.INSTANCE, RegExpFileType.INSTANCE); - private String myMimeType; - private FileType myContentFileType; - private FileType mySelectorFileType; + private final String myMimeType; + private final FileType myContentFileType; + private final FileType mySelectorFileType; + + private static Logger LOG = Logger.getInstance(ResponseType.class); + - ResponseType(final String s, final FileType contentFileType, final FileType selectorFileType) { + ResponseType(@NotNull String s, @NotNull FileType contentFileType, @NotNull FileType selectorFileType) { myMimeType = s; myContentFileType = contentFileType; mySelectorFileType = selectorFileType; } + /** + * Unfortunately XPATH instance can't be received this way, because XPathSupportLoader + * registers XPathFileType in FileTypeManager only in unit test and debug modes + */ + @NotNull + private static FileType findFileTypePlainDefault(@NotNull final String name) { + FileType fileType = FileTypeManager.getInstance().findFileTypeByName(name); + return fileType == null ? PlainTextFileType.INSTANCE : fileType; + } + + /** + * Temporary workaround for IDEA-112605 + */ + @NotNull + private static FileType findXPathFileType() { + if (LOG == null) { + LOG = Logger.getInstance(ResponseType.class); + } + try { + Class<?> xPathClass = Class.forName("org.intellij.lang.xpath.XPathFileType"); + LOG.debug("XPathFileType class loaded successfully"); + return (FileType)xPathClass.getField("XPATH").get(null); + } + catch (Exception e) { + LOG.debug("XPathFileType class not found. Using PlainText.INSTANCE instead"); + return PlainTextFileType.INSTANCE; + } + } + + @NotNull public String getMimeType() { return myMimeType; } + @NotNull public FileType getContentFileType() { return myContentFileType; } + @NotNull public FileType getSelectorFileType() { return mySelectorFileType; } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/SelectorBasedResponseHandler.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/SelectorBasedResponseHandler.java index 865e43db81a2..0394cc57094e 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/SelectorBasedResponseHandler.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/SelectorBasedResponseHandler.java @@ -1,5 +1,6 @@ package com.intellij.tasks.generic; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; @@ -24,6 +25,7 @@ import java.util.List; * @author Mikhail Golubev */ public abstract class SelectorBasedResponseHandler extends ResponseHandler { + private static final Logger LOG = Logger.getInstance(SelectorBasedResponseHandler.class); // Supported selector names @NonNls protected static final String TASKS = "tasks"; @@ -81,8 +83,6 @@ public abstract class SelectorBasedResponseHandler extends ResponseHandler { )); } - public abstract FileType getSelectorFileType(); - @Tag("selectors") @Property(surroundWithTag = false) @AbstractCollection(surroundWithTag = false) @@ -112,11 +112,11 @@ public abstract class SelectorBasedResponseHandler extends ResponseHandler { return s.getPath(); } + @NotNull @Override - public JComponent getConfigurationComponent(Project project) { - HighlightedSelectorsTable table = new HighlightedSelectorsTable(getSelectorFileType(), - project, - getSelectors()); + public JComponent getConfigurationComponent(@NotNull Project project) { + FileType fileType = getResponseType().getSelectorFileType(); + HighlightedSelectorsTable table = new HighlightedSelectorsTable(fileType, project, getSelectors()); return new JBScrollPane(table); } @@ -158,13 +158,14 @@ public abstract class SelectorBasedResponseHandler extends ResponseHandler { @NotNull @Override - public final Task[] parseIssues(String response, int max) throws Exception { + public final Task[] parseIssues(@NotNull String response, int max) throws Exception { if (StringUtil.isEmpty(getSelectorPath(TASKS)) || StringUtil.isEmpty(getSelectorPath(ID)) || StringUtil.isEmpty(getSelectorPath(SUMMARY))) { throw new Exception("Selectors 'tasks', 'id' and 'summary' are mandatory"); } List<Object> tasks = selectTasksList(response, max); + LOG.debug(String.format("Total %d tasks extracted from response", tasks.size())); List<Task> result = new ArrayList<Task>(tasks.size()); for (Object context : tasks) { String id = selectString(getSelector(ID), context); @@ -232,7 +233,7 @@ public abstract class SelectorBasedResponseHandler extends ResponseHandler { @Nullable @Override - public final Task parseIssue(String response) throws Exception { + public final Task parseIssue(@NotNull String response) throws Exception { if (StringUtil.isEmpty(getSelectorPath(SINGLE_TASK_ID)) || StringUtil.isEmpty(getSelectorPath(SINGLE_TASK_SUMMARY))) { throw new Exception("Selectors 'singleTask-id' and 'singleTask-summary' are mandatory"); diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/TemplateVariable.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/TemplateVariable.java index d8377bb49f59..f3b59fe2738a 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/TemplateVariable.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/TemplateVariable.java @@ -3,51 +3,42 @@ package com.intellij.tasks.generic; import com.intellij.util.xmlb.annotations.Attribute; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** - * User: evgeny.zakrevsky - * Date: 10/26/12 + * Editable variable which name can be used as placeholder and auto completed in EditorFields of + * {@link GenericRepositoryEditor}. Variables is editable via {@link ManageTemplateVariablesDialog}, + * but if {@code shownOnFirstTab} property was set, it will also be shown on "General" tab among + * standard fields like "Server URL", "Username" and "Password". + * + * @see GenericRepositoryEditor + * @see ManageTemplateVariablesDialog + * + * @author evgeny.zakrevsky + * @author Mikhail Golubev */ public class TemplateVariable { private String myName; private String myValue = ""; - private String myDescription; - private boolean myIsPredefined; - private boolean myIsHidden; - private boolean myIsShownOnFirstTab; + private String myDescription = ""; + private boolean myReadOnly; + private boolean myHidden; + private boolean myShownOnFirstTab; - public static TemplateVariableBuilder builder(String name) { - return new TemplateVariableBuilder(name); - } - - private TemplateVariable(TemplateVariableBuilder builder) { - myName = builder.myName; - myValue = builder.myValue; - myDescription = builder.myDescription; - myIsHidden = builder.myIsHidden; - myIsShownOnFirstTab = builder.myIsShowOnFirstTab; - myIsPredefined = builder.myIsPredefined; - } - - public TemplateVariable(String name, Object value) { - this(name, value, false, ""); - } - - public TemplateVariable(@NotNull @NonNls String name, @NotNull @NonNls Object value, boolean isPredefined, @Nullable String description) { + public TemplateVariable(@NotNull @NonNls String name, @NotNull @NonNls String value) { myName = name; myValue = String.valueOf(value); - myIsPredefined = isPredefined; - myDescription = description; + myReadOnly = false; + myDescription = ""; } /** * Serialization constructor */ + @SuppressWarnings("unusedDesclaration") public TemplateVariable() { + // empty } - /** * Cloning constructor */ @@ -55,157 +46,119 @@ public class TemplateVariable { myName = other.getName(); myValue = other.getValue(); myDescription = other.getDescription(); - myIsHidden = other.getIsHidden(); - myIsPredefined = other.getIsPredefined(); - myIsShownOnFirstTab = other.getIsShownOnFirstTab(); + myHidden = other.isHidden(); + myReadOnly = other.isReadOnly(); + myShownOnFirstTab = other.isShownOnFirstTab(); } - public void setName(String name) { + public void setName(@NotNull @NonNls String name) { myName = name; } - public void setValue(String value) { + public void setValue(@NotNull @NonNls String value) { myValue = value; } + @NotNull public String getName() { return myName; } + @NotNull public String getValue() { return myValue; } - @Nullable + // TODO: actually not used in UI + @NotNull public String getDescription() { return myDescription; } - @Attribute("isPredefined") - public boolean getIsPredefined() { - return myIsPredefined; + public void setDescription(@NotNull @NonNls String description) { + myDescription = description; + } + + @Attribute("readOnly") + public boolean isReadOnly() { + return myReadOnly; } - public void setIsPredefined(boolean isPredefined) { - myIsPredefined = isPredefined; + public void setReadOnly(boolean readOnly) { + myReadOnly = readOnly; } - @Attribute("isHidden") - public boolean getIsHidden() { - return myIsHidden; + @Attribute("hidden") + public boolean isHidden() { + return myHidden; } - public void setIsHidden(boolean isHidden) { - myIsHidden = isHidden; + public void setHidden(boolean hidden) { + myHidden = hidden; } @Attribute("shownOnFirstTab") - public boolean getIsShownOnFirstTab() { - return myIsShownOnFirstTab; + public boolean isShownOnFirstTab() { + return myShownOnFirstTab; } - public void setIsShownOnFirstTab(boolean isShownOnFirstTab) { - myIsShownOnFirstTab = isShownOnFirstTab; + public void setShownOnFirstTab(boolean shownOnFirstTab) { + myShownOnFirstTab = shownOnFirstTab; } public TemplateVariable clone() { return new TemplateVariable(this); } - public void setDescription(final String description) { - myDescription = description; - } - @Override public String toString() { return String.format("TemplateVariable(name='%s', value='%s')", getName(), getValue()); } - public static class TemplateVariableBuilder { - private String myName; - private String myValue = ""; - private String myDescription; - private boolean myIsHidden; - private boolean myIsPredefined; - private boolean myIsShowOnFirstTab; - - private TemplateVariableBuilder(String name) { - myName = name; - } - - public TemplateVariableBuilder value(Object value) { - myValue = String.valueOf(value); - return this; - } - - public TemplateVariableBuilder description(String description) { - myDescription = description; - return this; - } - - public TemplateVariableBuilder isHidden(boolean isHidden) { - myIsHidden = isHidden; - return this; - } - - public TemplateVariableBuilder isPredefined(boolean isPredefined) { - myIsPredefined = isPredefined; - return this; - } - - public TemplateVariableBuilder isShownOnFirstTab(boolean isShowOnFirstTab) { - myIsShowOnFirstTab = isShowOnFirstTab; - return this; - } - - public TemplateVariable build() { - return new TemplateVariable(this); - } - } - /** * Represents predefined template variable such as "serverUrl", "login" or "password" which are not * set explicitly by user but instead taken from repository itself. + * + * @see GenericRepository */ - public abstract static class PredefinedFactoryVariable extends TemplateVariable { + public abstract static class FactoryVariable extends TemplateVariable { - protected PredefinedFactoryVariable(String name) { + protected FactoryVariable(@NotNull @NonNls String name) { this(name, false); } - public PredefinedFactoryVariable(String name, boolean isHidden) { - this(name, name, isHidden); + public FactoryVariable(@NotNull @NonNls String name, boolean hidden) { + super(name, ""); + setHidden(hidden); } - public PredefinedFactoryVariable(String name, String description, boolean isHidden) { - super(builder(name).description(description).isHidden(isHidden)); - } + @NotNull @Override public abstract String getValue(); @Override - public final void setName(String name) { + public final void setName(@NotNull String name) { throw new UnsupportedOperationException("Name of predefined variable can't be changed"); } @Override - public final void setValue(String value) { + public final void setValue(@NotNull String value) { throw new UnsupportedOperationException("Value of predefined variable can't be changed explicitly"); } @Override - public final void setIsShownOnFirstTab(boolean isShownOnFirstTab) { + public final void setShownOnFirstTab(boolean shownOnFirstTab) { throw new UnsupportedOperationException("This parameter can't be changed for predefined variable"); } @Override - public void setIsPredefined(boolean isPredefined) { + public void setReadOnly(boolean readOnly) { throw new UnsupportedOperationException("This parameter can't be changed for predefined variable"); } @Override - public boolean getIsPredefined() { + public boolean isReadOnly() { return true; } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/XPathResponseHandler.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/XPathResponseHandler.java index f768258d817d..8d82ac189f50 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/XPathResponseHandler.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/XPathResponseHandler.java @@ -1,10 +1,8 @@ package com.intellij.tasks.generic; -import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.HashMap; import com.intellij.util.xmlb.annotations.Tag; -import org.intellij.lang.xpath.XPathFileType; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; @@ -79,11 +77,7 @@ public final class XPathResponseHandler extends SelectorBasedResponseHandler { return myCompiledCache.get(path); } - @Override - public FileType getSelectorFileType() { - return XPathFileType.XPATH; - } - + @NotNull @Override public ResponseType getResponseType() { return ResponseType.XML; diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/connectors/asana.xml b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/connectors/asana.xml index 942ca6f7f68a..94d69c153be1 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/connectors/asana.xml +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/connectors/asana.xml @@ -55,8 +55,8 @@ <option name="tasksListUrl" value="{serverUrl}/projects/{project_ID}/tasks?assignee=me"/> <option name="templateVariables"> <list> - <TemplateVariable isHidden="false" isPredefined="false" shownOnFirstTab="true"> - <option name="description"/> + <TemplateVariable hidden="false" readOnly="false" shownOnFirstTab="true"> + <option name="description" value=""/> <option name="name" value="project_ID"/> <option name="value" value=""/> </TemplateVariable> diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/connectors/assembla.xml b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/connectors/assembla.xml new file mode 100644 index 000000000000..da466a6014c8 --- /dev/null +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/connectors/assembla.xml @@ -0,0 +1,64 @@ +<Assembla shared="false" url="http://www.assembla.com"> + <commitMessageFormat>{id} {summary}</commitMessageFormat> + <option name="downloadTasksInSeparateRequests" value="false" /> + <password/> + <option name="loginAnonymously" value="false" /> + <option name="loginMethodType" value="GET" /> + <option name="loginUrl" value="" /> + <option name="responseHandlers"> + <XPathResponseHandler> + <selectors> + <selector name="tasks" path="/tickets/ticket" /> + <selector name="id" path="id" /> + <selector name="summary" path="summary" /> + <selector name="description" path="description" /> + <selector name="updated" path="updated-at" /> + <selector name="created" path="created-on" /> + <selector name="closed" path="" /> + <selector name="issueUrl" path="" /> + <selector name="singleTask-id" path="" /> + <selector name="singleTask-summary" path="" /> + <selector name="singleTask-description" path="" /> + <selector name="singleTask-updated" path="" /> + <selector name="singleTask-created" path="" /> + <selector name="singleTask-closed" path="" /> + <selector name="singleTask-issueUrl" path="" /> + </selectors> + </XPathResponseHandler> + <JsonResponseHandler> + <selectors> + <selector name="tasks" path="" /> + <selector name="id" path="" /> + <selector name="summary" path="" /> + <selector name="description" path="" /> + <selector name="updated" path="" /> + <selector name="created" path="" /> + <selector name="closed" path="" /> + <selector name="issueUrl" path="" /> + <selector name="singleTask-id" path="" /> + <selector name="singleTask-summary" path="" /> + <selector name="singleTask-description" path="" /> + <selector name="singleTask-updated" path="" /> + <selector name="singleTask-created" path="" /> + <selector name="singleTask-closed" path="" /> + <selector name="singleTask-issueUrl" path="" /> + </selectors> + </JsonResponseHandler> + <RegExResponseHandler> + <option name="taskRegex" value=""/> + </RegExResponseHandler> + </option> + <option name="responseType" value="XML" /> + <option name="shouldFormatCommitMessage" value="false" /> + <option name="singleTaskMethodType" value="GET" /> + <option name="singleTaskUrl" value="" /> + <option name="subtypeName" /> + <option name="tasksListMethodType" value="GET" /> + <option name="tasksListUrl" value="http://www.assembla.com/tickets.xml" /> + <option name="templateVariables"> + <list /> + </option> + <option name="useHttpAuthentication" value="true" /> + <option name="useProxy" value="false" /> + <username/> +</Assembla> diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlElementType.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlElementType.java index fde826e4a62c..9d9dc9118997 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlElementType.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlElementType.java @@ -15,22 +15,49 @@ */ package com.intellij.tasks.jira.jql; +import com.intellij.extapi.psi.ASTWrapperPsiElement; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import java.lang.reflect.Constructor; + /** * @author Mikhail Golubev */ public class JqlElementType extends IElementType { - private String myName; + private static final Class<?>[] PARAMETER_TYPES = {ASTNode.class}; + + private final Class<? extends PsiElement> myClass; + private Constructor<? extends PsiElement> myConstructor; + public JqlElementType(@NotNull @NonNls String debugName) { + this(debugName, ASTWrapperPsiElement.class); + } + + public JqlElementType(@NotNull @NonNls String debugName, @NotNull Class<? extends PsiElement> cls) { super(debugName, JqlLanguage.INSTANCE); - myName = debugName; + myClass = cls; } @Override public String toString() { return "JQL: " + super.toString(); } + + @NotNull + public PsiElement createElement(@NotNull ASTNode node) { + try { + if (myConstructor == null) { + myConstructor = myClass.getConstructor(PARAMETER_TYPES); + } + return myConstructor.newInstance(node); + } + catch (Exception e) { + throw new AssertionError( + String.format("Class %s must have constructor accepting single ASTNode parameter", myClass.getName())); + } + } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlElementTypes.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlElementTypes.java index 68a989335cb4..9919eb6d1d85 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlElementTypes.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlElementTypes.java @@ -3,6 +3,7 @@ package com.intellij.tasks.jira.jql; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IFileElementType; import com.intellij.psi.tree.TokenSet; +import com.intellij.tasks.jira.jql.psi.impl.*; /** * @author Mikhail Golubev @@ -14,8 +15,9 @@ import com.intellij.psi.tree.TokenSet; * or_clause ::= and_clause {or_op and_clause} * and_clause ::= not_expr {and_op not_expr} * not_expr ::= not_op not_expr - * | "(" or_clause ")" + * | subclause * | terminal_clause + * subclause ::= "(" or_clause ")" * terminal_clause ::= simple_clause * | was_clause * | changed_clause @@ -46,11 +48,12 @@ import com.intellij.psi.tree.TokenSet; * field ::= string * | NUMBER * | CUSTOM_FIELD - * operand ::= ("empty" | "null") + * operand ::= empty * | string * | NUMBER * | func * | list + * empty ::= "empty" | "null" * list ::= "(" operand {"," operand} ")" * func ::= fname "(" arg_list ")" * # function name can be even number (!) @@ -58,7 +61,7 @@ import com.intellij.psi.tree.TokenSet; * arg_list ::= argument {"," argument} * argument ::= string | NUMBER * string ::= SQUOTED_STRING - * | QUOTED_STRIN + * | QUOTED_STRING * | UNQOUTED_STRING * order_by ::= "order" "by" sort_key {sort_key} * sort_key ::= field ("asc" | "desc") @@ -66,26 +69,28 @@ import com.intellij.psi.tree.TokenSet; */ public interface JqlElementTypes { IFileElementType FILE = new IFileElementType(JqlLanguage.INSTANCE); - IElementType QUERY = new JqlElementType("QUERY"); - IElementType OR_CLAUSE = new JqlElementType("OR_CLAUSE"); - IElementType AND_CLAUSE = new JqlElementType("AND_CLAUSE"); - IElementType NOT_CLAUSE = new JqlElementType("NOT_CLAUSE"); + IElementType QUERY = new JqlElementType("QUERY", JqlQueryImpl.class); + IElementType OR_CLAUSE = new JqlElementType("OR_CLAUSE", JqlOrClauseImpl.class); + IElementType AND_CLAUSE = new JqlElementType("AND_CLAUSE", JqlAndClauseImpl.class); + IElementType NOT_CLAUSE = new JqlElementType("NOT_CLAUSE", JqlNotClauseImpl.class); + // actually parenthesized clause, named so to be consistent with official grammar + IElementType SUB_CLAUSE = new JqlElementType("SUB_CLAUSE", JqlSubClauseImpl.class); //IElementType TERMINAL_CLAUSE = new JqlElementType("TERMINAL_CLAUSE"); // field (= | != | ~ | !~ | < | > | <= | >= | is [not] | [not] in) value - IElementType SIMPLE_CLAUSE = new JqlElementType("SIMPLE_CLAUSE"); + IElementType SIMPLE_CLAUSE = new JqlElementType("SIMPLE_CLAUSE", JqlSimpleClauseImpl.class); // field was [not] [in] value {history_predicate} - IElementType WAS_CLAUSE = new JqlElementType("WAS_CLAUSE"); + IElementType WAS_CLAUSE = new JqlElementType("WAS_CLAUSE", JqlWasClauseImpl.class); // field changed {history_predicate} - IElementType CHANGED_CLAUSE = new JqlElementType("CHANGED_CLAUSE"); - IElementType LIST = new JqlElementType("LIST"); - IElementType ORDER_BY = new JqlElementType("ORDER_BY"); - IElementType IDENTIFIER = new JqlElementType("IDENTIFIER"); - IElementType LITERAL = new JqlElementType("LITERAL"); - IElementType FUNCTION_CALL = new JqlElementType("FUNCTION_CALL"); - IElementType ARGUMENT_LIST = new JqlElementType("ARGUMENT_LIST"); - IElementType SORT_KEY = new JqlElementType("SORT_KEY"); - IElementType EMPTY = new JqlElementType("EMPTY"); - IElementType HISTORY_PREDICATE = new JqlElementType("HISTORY_PREDICATE"); + IElementType CHANGED_CLAUSE = new JqlElementType("CHANGED_CLAUSE", JqlChangedClauseImpl.class); + IElementType LIST = new JqlElementType("LIST", JqlListImpl.class); + IElementType ORDER_BY = new JqlElementType("ORDER_BY", JqlOrderByImpl.class); + IElementType IDENTIFIER = new JqlElementType("IDENTIFIER", JqlIdentifierImpl.class); + IElementType LITERAL = new JqlElementType("LITERAL", JqlLiteralImpl.class); + IElementType FUNCTION_CALL = new JqlElementType("FUNCTION_CALL", JqlFunctionCallImpl.class); + IElementType ARGUMENT_LIST = new JqlElementType("ARGUMENT_LIST", JqlArgumentListImpl.class); + IElementType SORT_KEY = new JqlElementType("SORT_KEY", JqlSortKeyImpl.class); + IElementType EMPTY = new JqlElementType("EMPTY", JqlEmptyValueImpl.class); + IElementType HISTORY_PREDICATE = new JqlElementType("HISTORY_PREDICATE", JqlHistoryPredicateImpl.class); TokenSet OPERAND_NODES = TokenSet.create( JqlTokenTypes.NUMBER_LITERAL, JqlTokenTypes.STRING_LITERAL, LIST, FUNCTION_CALL diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlParser.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlParser.java index 1dcac3633cd1..2021bc943ae0 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlParser.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlParser.java @@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull; * @author Mikhail Golubev */ public class JqlParser implements PsiParser { - private static final Logger LOG = Logger.getInstance("#com.intellij.tasks.jira.jql.JqlParser"); + private static final Logger LOG = Logger.getInstance(JqlParser.class); @NotNull @Override @@ -92,16 +92,17 @@ public class JqlParser implements PsiParser { } private boolean parseSubClause(PsiBuilder builder) { + LOG.assertTrue(builder.getTokenType() == JqlTokenTypes.LPAR); + PsiBuilder.Marker marker = builder.mark(); if (!advanceIfMatches(builder, JqlTokenTypes.LPAR)) { + marker.drop(); return false; } - if (!parseORClause(builder)) { - return false; - } + parseORClause(builder); if (!advanceIfMatches(builder, JqlTokenTypes.RPAR)) { builder.error("Expecting ')'"); - return false; } + marker.done(JqlElementTypes.SUB_CLAUSE); return true; } @@ -142,6 +143,7 @@ public class JqlParser implements PsiParser { } private void parseCHANGEDClause(PsiBuilder builder) { + LOG.assertTrue(builder.getTokenType() == JqlTokenTypes.CHANGED_KEYWORD); if (!advanceIfMatches(builder, JqlTokenTypes.CHANGED_KEYWORD)) { return; } @@ -151,6 +153,7 @@ public class JqlParser implements PsiParser { } private void parseWASClause(PsiBuilder builder) { + LOG.assertTrue(builder.getTokenType() == JqlTokenTypes.WAS_KEYWORD); if (!advanceIfMatches(builder, JqlTokenTypes.WAS_KEYWORD)) { return; } @@ -163,6 +166,7 @@ public class JqlParser implements PsiParser { } private void parseHistoryPredicate(PsiBuilder builder) { + LOG.assertTrue(JqlTokenTypes.HISTORY_PREDICATES.contains(builder.getTokenType())); PsiBuilder.Marker marker = builder.mark(); if (!advanceIfMatches(builder, JqlTokenTypes.HISTORY_PREDICATES)) { marker.drop(); @@ -204,6 +208,7 @@ public class JqlParser implements PsiParser { } private boolean parseList(PsiBuilder builder) { + LOG.assertTrue(builder.getTokenType() == JqlTokenTypes.LPAR); PsiBuilder.Marker marker = builder.mark(); if (!advanceIfMatches(builder, JqlTokenTypes.LPAR)) { marker.drop(); @@ -235,6 +240,7 @@ public class JqlParser implements PsiParser { } private void parseArgumentList(PsiBuilder builder) { + LOG.assertTrue(builder.getTokenType() == JqlTokenTypes.LPAR); PsiBuilder.Marker marker = builder.mark(); if (!advanceIfMatches(builder, JqlTokenTypes.LPAR)) { marker.drop(); diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlParserDefinition.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlParserDefinition.java index 7e3eb55dffbf..a8269625fcfd 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlParserDefinition.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlParserDefinition.java @@ -13,7 +13,6 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IFileElementType; import com.intellij.psi.tree.TokenSet; -import com.intellij.tasks.jira.jql.psi.impl.*; import org.jetbrains.annotations.NotNull; /** @@ -59,54 +58,9 @@ public class JqlParserDefinition implements ParserDefinition { @NotNull @Override public PsiElement createElement(ASTNode node) { - IElementType type = node.getElementType(); - if (type == JqlElementTypes.QUERY) { - return new JqlQueryImpl(node); - } - if (type == JqlElementTypes.AND_CLAUSE) { - return new JqlAndClauseImpl(node); - } - if (type == JqlElementTypes.OR_CLAUSE) { - return new JqlOrClauseImpl(node); - } - if (type == JqlElementTypes.NOT_CLAUSE) { - return new JqlNotClauseImpl(node); - } - if (type == JqlElementTypes.SIMPLE_CLAUSE) { - return new JqlSimpleClauseImpl(node); - } - if (type == JqlElementTypes.WAS_CLAUSE) { - return new JqlWasClauseImpl(node); - } - if (type == JqlElementTypes.CHANGED_CLAUSE) { - return new JqlChangedClauseImpl(node); - } - if (type == JqlElementTypes.FUNCTION_CALL) { - return new JqlFunctionCallImpl(node); - } - if (type == JqlElementTypes.IDENTIFIER) { - return new JqlIdentifierImpl(node); - } - if (type == JqlElementTypes.HISTORY_PREDICATE) { - return new JqlHistoryPredicateImpl(node); - } - if (type == JqlElementTypes.ARGUMENT_LIST) { - return new JqlArgumentListImpl(node); - } - if (type == JqlElementTypes.LITERAL) { - return new JqlLiteralImpl(node); - } - if (type == JqlElementTypes.EMPTY) { - return new JqlEmptyValueImpl(node); - } - if (type == JqlElementTypes.LIST) { - return new JqlListImpl(node); - } - if (type == JqlElementTypes.SORT_KEY) { - return new JqlSortKeyImpl(node); - } - if (type == JqlElementTypes.ORDER_BY) { - return new JqlOrderByImpl(node); + final IElementType type = node.getElementType(); + if (type instanceof JqlElementType) { + return ((JqlElementType)type).createElement(node); } return new ASTWrapperPsiElement(node); } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlTokenTypes.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlTokenTypes.java index 1d0c6f1c6a0e..ddb8bdf577a6 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlTokenTypes.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/JqlTokenTypes.java @@ -104,12 +104,6 @@ public interface JqlTokenTypes { TokenSet HISTORY_PREDICATES = TokenSet.create( ON_KEYWORD, BEFORE_KEYWORD, AFTER_KEYWORD, DURING_KEYWORD, FROM_KEYWORD, TO_KEYWORD, BY_KEYWORD ); - TokenSet WAS_CONSTRAINTS = TokenSet.create( - ON_KEYWORD, BEFORE_KEYWORD, AFTER_KEYWORD, DURING_KEYWORD, BY_KEYWORD - ); - TokenSet CHANGED_CONSTRAINTS = TokenSet.create( - ON_KEYWORD, BEFORE_KEYWORD, AFTER_KEYWORD, DURING_KEYWORD, FROM_KEYWORD, TO_KEYWORD, BY_KEYWORD - ); TokenSet SORT_ORDERS = TokenSet.create(ASC_KEYWORD, DESC_KEYWORD); TokenSet EMPTY_VALUES = TokenSet.create(EMPTY_KEYWORD, NULL_KEYWORD); TokenSet LITERALS = TokenSet.create(NUMBER_LITERAL, STRING_LITERAL); diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/codeinsight/JqlCompletionContributor.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/codeinsight/JqlCompletionContributor.java index 484a357bc0c1..e243402b14c0 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/codeinsight/JqlCompletionContributor.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/codeinsight/JqlCompletionContributor.java @@ -55,15 +55,15 @@ public class JqlCompletionContributor extends CompletionContributor { if (!(element instanceof PsiElement)) return false; PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf((PsiElement)element); if (prevLeaf == null) return false; - PsiElement enclosingClause = PsiTreeUtil.findFirstParent(prevLeaf, new Condition<PsiElement>() { + PsiElement parent = PsiTreeUtil.findFirstParent(prevLeaf, new Condition<PsiElement>() { @Override public boolean value(PsiElement element) { return pattern.accepts(element); } }); - if (enclosingClause == null) return false; - if (PsiTreeUtil.hasErrorElements(enclosingClause)) return false; - return prevLeaf.getTextRange().getEndOffset() == enclosingClause.getTextRange().getEndOffset(); + if (parent == null) return false; + if (PsiTreeUtil.hasErrorElements(parent)) return false; + return prevLeaf.getTextRange().getEndOffset() == parent.getTextRange().getEndOffset(); } @Override @@ -83,7 +83,10 @@ public class JqlCompletionContributor extends CompletionContributor { psiElement().and(rightAfterElement(JqlClauseWithHistoryPredicates.class)); private static final PsiElementPattern.Capture<PsiElement> AFTER_ANY_CLAUSE = - psiElement().and(rightAfterElement(JqlTerminalClause.class)); + psiElement().andOr( + rightAfterElement(JqlTerminalClause.class), + // in other words after closing parenthesis + rightAfterElement(JqlSubClause.class)); private static final PsiElementPattern.Capture<PsiElement> AFTER_ORDER_KEYWORD = psiElement().afterLeaf(psiElement(JqlTokenTypes.ORDER_KEYWORD)); @@ -263,7 +266,6 @@ public class JqlCompletionContributor extends CompletionContributor { JqlFieldType operandType; boolean listFunctionExpected; PsiElement curElem = parameters.getPosition(); - PsiElement origElem = parameters.getOriginalPosition(); JqlHistoryPredicate predicate = PsiTreeUtil.getParentOfType(curElem, JqlHistoryPredicate.class); if (predicate != null) { listFunctionExpected = false; diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/JqlElementVisitor.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/JqlElementVisitor.java index e0dc6895cb44..bbf71debe8c0 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/JqlElementVisitor.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/JqlElementVisitor.java @@ -3,6 +3,7 @@ package com.intellij.tasks.jira.jql.psi; import com.intellij.psi.PsiElementVisitor; import com.intellij.tasks.jira.jql.psi.impl.JqlArgumentListImpl; import com.intellij.tasks.jira.jql.psi.impl.JqlHistoryPredicateImpl; +import com.intellij.tasks.jira.jql.psi.impl.JqlSubClauseImpl; /** * @author Mikhail Golubev @@ -40,4 +41,6 @@ public abstract class JqlElementVisitor extends PsiElementVisitor { public abstract void visitJqlArgumentList(JqlArgumentListImpl list); public abstract void visitJqlHistoryPredicate(JqlHistoryPredicateImpl predicate); + + public abstract void visitJqlSubClause(JqlSubClauseImpl subClause); } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/JqlQuery.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/JqlQuery.java index b832b9d684c3..79bb3bfee8f8 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/JqlQuery.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/JqlQuery.java @@ -9,7 +9,7 @@ import org.jetbrains.annotations.Nullable; public interface JqlQuery extends JqlElement { @Nullable JqlClause getClause(); - boolean isOrdered(); - @NotNull - JqlSortKey[] getOrderKeys(); + + @Nullable + JqlOrderBy getOrderBy(); } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/JqlSubClause.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/JqlSubClause.java new file mode 100644 index 000000000000..c28de2880d57 --- /dev/null +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/JqlSubClause.java @@ -0,0 +1,11 @@ +package com.intellij.tasks.jira.jql.psi; + +import org.jetbrains.annotations.Nullable; + +/** + * @author Mikhail Golubev + */ +public interface JqlSubClause extends JqlClause { + @Nullable + JqlClause getInnerClause(); +} diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/JqlTerminalClause.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/JqlTerminalClause.java index d16a1064e404..f44016189fb1 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/JqlTerminalClause.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/JqlTerminalClause.java @@ -10,7 +10,7 @@ import java.util.IdentityHashMap; /** * @author Mikhail Golubev */ -public interface JqlTerminalClause extends JqlElement { +public interface JqlTerminalClause extends JqlClause { enum Type { EQ(false), NE(false), diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/impl/JqlQueryImpl.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/impl/JqlQueryImpl.java index c9ed11a745f1..0565800e8e1a 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/impl/JqlQueryImpl.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/impl/JqlQueryImpl.java @@ -1,11 +1,9 @@ package com.intellij.tasks.jira.jql.psi.impl; import com.intellij.lang.ASTNode; -import com.intellij.tasks.jira.jql.psi.JqlClause; -import com.intellij.tasks.jira.jql.psi.JqlElementVisitor; -import com.intellij.tasks.jira.jql.psi.JqlSortKey; -import com.intellij.tasks.jira.jql.psi.JqlQuery; +import com.intellij.tasks.jira.jql.psi.*; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author Mikhail Golubev @@ -25,13 +23,9 @@ public class JqlQueryImpl extends JqlElementImpl implements JqlQuery { return findChildByClass(JqlClause.class); } + @Nullable @Override - public boolean isOrdered() { - return getOrderKeys().length != 0; - } - - @Override - public JqlSortKey[] getOrderKeys() { - return findChildrenByClass(JqlSortKey.class); + public JqlOrderBy getOrderBy() { + return findChildByClass(JqlOrderBy.class); } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/impl/JqlSubClauseImpl.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/impl/JqlSubClauseImpl.java new file mode 100644 index 000000000000..05f959a17e09 --- /dev/null +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/jira/jql/psi/impl/JqlSubClauseImpl.java @@ -0,0 +1,28 @@ +package com.intellij.tasks.jira.jql.psi.impl; + +import com.intellij.lang.ASTNode; +import com.intellij.tasks.jira.jql.psi.JqlClause; +import com.intellij.tasks.jira.jql.psi.JqlElementVisitor; +import com.intellij.tasks.jira.jql.psi.JqlSubClause; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Mikhail Golubev + */ +public class JqlSubClauseImpl extends JqlElementImpl implements JqlSubClause { + public JqlSubClauseImpl(@NotNull ASTNode node) { + super(node); + } + + @Override + public void accept(JqlElementVisitor visitor) { + visitor.visitJqlSubClause(this); + } + + @Nullable + @Override + public JqlClause getInnerClause() { + return findChildByClass(JqlClause.class); + } +} diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java index f1571fb61447..697e71be7a02 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java @@ -185,10 +185,10 @@ public class RedmineRepository extends BaseRepositoryImpl { // Ad-hoc fix for IDEA-110012 Date parsed; try { - parsed = (new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.US)).parse(date); + parsed = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.US).parse(date); } catch (ParseException e) { - LOG.warn("Unparseable date: " + date, e); + LOG.warn("Unparseable date: '" + date + "'. Trying ISO-8601 format instead."); parsed = TaskUtil.parseDate(date); } return parsed; diff --git a/plugins/tasks/tasks-core/tasks-core.iml b/plugins/tasks/tasks-core/tasks-core.iml index 11c3bc9c626a..460f43fcfe5c 100644 --- a/plugins/tasks/tasks-core/tasks-core.iml +++ b/plugins/tasks/tasks-core/tasks-core.iml @@ -39,7 +39,6 @@ <orderEntry type="library" name="gson" level="project" /> <orderEntry type="module" module-name="xml" /> <orderEntry type="module" module-name="core-api" /> - <orderEntry type="module" module-name="xpath" /> <orderEntry type="module" module-name="RegExpSupport" /> <orderEntry type="module-library"> <library> diff --git a/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskUtilTest.java b/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskUtilTest.java new file mode 100644 index 000000000000..def2a678f2da --- /dev/null +++ b/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskUtilTest.java @@ -0,0 +1,51 @@ +package com.intellij.tasks; + +import com.intellij.tasks.impl.TaskUtil; +import org.jetbrains.annotations.NotNull; +import org.junit.Test; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +/** + * @author Mikhail Golubev + */ +public class TaskUtilTest { + private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + static { + FORMATTER.setTimeZone(TimeZone.getTimeZone("GMT")); + } + + private static void compareDates(@NotNull Date expected, @NotNull String formattedDate) { + Date parsed = TaskUtil.parseDate(formattedDate); + assertEquals(expected, parsed); + } + + /** + * Test ISO8601 date parsing + */ + @Test + public void testDateParsings() throws Exception { + final Date expected = FORMATTER.parse("2013-08-23 10:11:12.000"); + final Date expectedWithMillis = FORMATTER.parse("2013-08-23 10:11:12.100"); + // JIRA, Redmine and Pivotal + compareDates(expectedWithMillis, "2013-08-23T14:11:12.100+0400"); + // Trello + compareDates(expectedWithMillis, "2013-08-23T10:11:12.100Z"); + // Assmbla + compareDates(expectedWithMillis, "2013-08-23T14:11:12.100+04:00"); + + // Formatting variations + compareDates(expected, "2013/08/23 10:11:12"); + compareDates(expectedWithMillis, "2013-08-23 14:11:12.100123+04"); + + // Malformed date + assertNull(TaskUtil.parseDate("Fri Aug 23 14:11:12 MSK 2013")); + assertNull(TaskUtil.parseDate("2013/00/23")); + assertNull(TaskUtil.parseDate("2013/08/23 10:11:12 GMT+04:00")); + } +}
\ No newline at end of file diff --git a/plugins/tasks/tasks-tests/test/com/intellij/tasks/context/ContextTest.java b/plugins/tasks/tasks-tests/test/com/intellij/tasks/context/ContextTest.java index 98a3dd05944e..029b9a7d208e 100644 --- a/plugins/tasks/tasks-tests/test/com/intellij/tasks/context/ContextTest.java +++ b/plugins/tasks/tasks-tests/test/com/intellij/tasks/context/ContextTest.java @@ -17,6 +17,7 @@ package com.intellij.tasks.context; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.impl.ProjectImpl; import com.intellij.openapi.util.io.FileUtil; import com.intellij.tasks.TaskManagerTestCase; import com.intellij.xdebugger.XDebuggerManager; @@ -93,6 +94,18 @@ public class ContextTest extends TaskManagerTestCase { manager.clearContext(); } + public void testContextFileName() throws Exception { + ProjectImpl project = (ProjectImpl)getProject(); + String name = project.getName(); + try { + project.setProjectName("invalid | name"); + getContextManager().saveContext("foo", "bar"); + } + finally { + project.setProjectName(name); + } + } + private WorkingContextManager getContextManager() { return WorkingContextManager.getInstance(getProject()); } diff --git a/plugins/tasks/tasks-tests/test/com/intellij/tasks/jira/jql/CompletionTest.java b/plugins/tasks/tasks-tests/test/com/intellij/tasks/jira/jql/CompletionTest.java index d3ee5b12c5f6..6269e48deb8e 100644 --- a/plugins/tasks/tasks-tests/test/com/intellij/tasks/jira/jql/CompletionTest.java +++ b/plugins/tasks/tasks-tests/test/com/intellij/tasks/jira/jql/CompletionTest.java @@ -104,10 +104,14 @@ public class CompletionTest extends CodeInsightFixtureTestCase { checkCompletionVariants(JqlStandardFunction.allOfType(JqlFieldType.DATE, false)); } - public void testAfterParenthesisInSubClause() throws Exception { + public void testAfterLeftParenthesisInSubClause() throws Exception { checkCompletionVariants(ALL_FIELD_NAMES, "not"); } + public void testAfterSubClause() throws Exception { + checkCompletionVariants("and", "or", "order by"); + } + public void testFunctionArguments() throws Exception { // only literals accepted so we can't assume anything checkCompletionVariants(ContainerUtil.<String>emptyList()); diff --git a/plugins/tasks/tasks-tests/test/com/intellij/tasks/vcs/TaskBranchesTest.java b/plugins/tasks/tasks-tests/test/com/intellij/tasks/vcs/TaskBranchesTest.java index 1261d1239180..36cc584ba1a4 100644 --- a/plugins/tasks/tasks-tests/test/com/intellij/tasks/vcs/TaskBranchesTest.java +++ b/plugins/tasks/tasks-tests/test/com/intellij/tasks/vcs/TaskBranchesTest.java @@ -15,10 +15,17 @@ */ package com.intellij.tasks.vcs; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.VcsTaskHandler; +import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.tasks.BranchInfo; import com.intellij.tasks.LocalTask; import com.intellij.tasks.TaskManager; +import com.intellij.tasks.actions.OpenTaskDialog; import com.intellij.tasks.impl.LocalTaskImpl; import com.intellij.tasks.impl.TaskManagerImpl; import com.intellij.testFramework.PlatformTestCase; @@ -29,8 +36,10 @@ import git4idea.config.GitVcsSettings; import git4idea.repo.GitRepository; import git4idea.test.GitExecutor; import git4idea.test.GitTestUtil; +import git4idea.util.GitFileUtils; import java.io.File; +import java.util.Collections; import java.util.List; /** @@ -41,6 +50,8 @@ import java.util.List; public class TaskBranchesTest extends PlatformTestCase { private TaskManagerImpl myTaskManager; + private ChangeListManagerImpl myChangeListManager; + private VcsDirtyScopeManagerImpl myDirtyScopeManager; public void testGitTaskHandler() throws Exception { @@ -104,6 +115,46 @@ public class TaskBranchesTest extends PlatformTestCase { myTaskManager.activateTask(foo, false); } + public void testCommit() throws Exception { + GitRepository repository = initRepository("foo"); + LocalTask defaultTask = myTaskManager.getActiveTask(); + LocalTaskImpl foo = myTaskManager.createLocalTask("foo"); + final LocalTask localTask = myTaskManager.activateTask(foo, false); + myTaskManager.createBranch(localTask, defaultTask, myTaskManager.suggestBranchName(localTask)); + + VirtualFile root = repository.getRoot(); + File file = new File(root.getPath(), "foo.txt"); + assertTrue(file.createNewFile()); + final VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); + GitFileUtils.addFiles(getProject(), root, virtualFile); + myDirtyScopeManager.fileDirty(virtualFile); + myChangeListManager.ensureUpToDate(false); + Change change = myChangeListManager.getChange(virtualFile); + assertNotNull(change); + ProjectLevelVcsManager.getInstance(getProject()).getAllActiveVcss()[0].getCheckinEnvironment() + .commit(Collections.singletonList(change), "foo"); + myTaskManager.mergeBranch(localTask); + + repository.update(); + assertEquals("master", repository.getCurrentBranch().getName()); + assertEquals(1, repository.getBranches().getLocalBranches().size()); + } + + public void testOpenTaskDialog() throws Exception { + initRepository("foo"); + LocalTaskImpl task = myTaskManager.createLocalTask("foo"); + OpenTaskDialog dialog = new OpenTaskDialog(getProject(), task); + Disposer.register(myTestRootDisposable, dialog.getDisposable()); + dialog.createTask(); + assertEquals("foo", myTaskManager.getActiveTask().getSummary()); + List<BranchInfo> branches = task.getBranches(true); + assertEquals(1, branches.size()); + assertEquals("master", branches.get(0).name); + branches = task.getBranches(false); + assertEquals(1, branches.size()); + assertEquals("foo", branches.get(0).name); + } + private List<GitRepository> initRepositories(String... names) { return ContainerUtil.map(names, new Function<String, GitRepository>() { @Override @@ -128,5 +179,10 @@ public class TaskBranchesTest extends PlatformTestCase { super.setUp(); myTaskManager = (TaskManagerImpl)TaskManager.getManager(getProject()); GitVcsSettings.getInstance(myProject).getAppSettings().setPathToGit(GitExecutor.GIT_EXECUTABLE); + + myChangeListManager = (ChangeListManagerImpl)ChangeListManager.getInstance(getProject()); + myChangeListManager.projectOpened(); + myDirtyScopeManager = ((VcsDirtyScopeManagerImpl)VcsDirtyScopeManager.getInstance(getProject())); + myDirtyScopeManager.projectOpened(); } } diff --git a/plugins/tasks/tasks-tests/testData/jira/jql/completion/AfterParenthesisInSubClause.jql b/plugins/tasks/tasks-tests/testData/jira/jql/completion/AfterLeftParenthesisInSubClause.jql index 66abe15b5913..66abe15b5913 100644 --- a/plugins/tasks/tasks-tests/testData/jira/jql/completion/AfterParenthesisInSubClause.jql +++ b/plugins/tasks/tasks-tests/testData/jira/jql/completion/AfterLeftParenthesisInSubClause.jql diff --git a/plugins/tasks/tasks-tests/testData/jira/jql/completion/AfterSubClause.jql b/plugins/tasks/tasks-tests/testData/jira/jql/completion/AfterSubClause.jql new file mode 100644 index 000000000000..d2ce0ae79af5 --- /dev/null +++ b/plugins/tasks/tasks-tests/testData/jira/jql/completion/AfterSubClause.jql @@ -0,0 +1 @@ +(reporter was in (bob, mark)) <caret>
\ No newline at end of file diff --git a/plugins/tasks/tasks-tests/testData/jira/jql/psi/Subclauses.txt b/plugins/tasks/tasks-tests/testData/jira/jql/psi/Subclauses.txt index d004bc4a6360..3ec6387b3293 100644 --- a/plugins/tasks/tasks-tests/testData/jira/jql/psi/Subclauses.txt +++ b/plugins/tasks/tasks-tests/testData/jira/jql/psi/Subclauses.txt @@ -1,46 +1,50 @@ FILE(0,50) JqlQuery((a = foo or (b > 12 and ((c < 40)))) and d ~ 'foo')(0,50) JqlAndClause((a = foo or (b > 12 and ((c < 40)))) and d ~ 'foo')(0,50) - PsiElement(JQL: LPAR)('(')(0,1) - JqlOrClause(a = foo or (b > 12 and ((c < 40))))(1,35) - JqlSimpleClause(a = foo)(1,8) - JqlIdentifier(a)(1,2) - PsiElement(JQL: STRING_LITERAL)('a')(1,2) - PsiWhiteSpace(' ')(2,3) - PsiElement(JQL: EQ)('=')(3,4) - PsiWhiteSpace(' ')(4,5) - JqlLiteral(foo)(5,8) - PsiElement(JQL: STRING_LITERAL)('foo')(5,8) - PsiWhiteSpace(' ')(8,9) - PsiElement(JQL: OR_KEYWORD)('or')(9,11) - PsiWhiteSpace(' ')(11,12) - PsiElement(JQL: LPAR)('(')(12,13) - JqlAndClause(b > 12 and ((c < 40)))(13,34) - JqlSimpleClause(b > 12)(13,19) - JqlIdentifier(b)(13,14) - PsiElement(JQL: STRING_LITERAL)('b')(13,14) - PsiWhiteSpace(' ')(14,15) - PsiElement(JQL: GT)('>')(15,16) - PsiWhiteSpace(' ')(16,17) - JqlLiteral(12)(17,19) - PsiElement(JQL: NUMBER_LITERAL)('12')(17,19) - PsiWhiteSpace(' ')(19,20) - PsiElement(JQL: AND_KEYWORD)('and')(20,23) - PsiWhiteSpace(' ')(23,24) - PsiElement(JQL: LPAR)('(')(24,25) - PsiElement(JQL: LPAR)('(')(25,26) - JqlSimpleClause(c < 40)(26,32) - JqlIdentifier(c)(26,27) - PsiElement(JQL: STRING_LITERAL)('c')(26,27) - PsiWhiteSpace(' ')(27,28) - PsiElement(JQL: LT)('<')(28,29) - PsiWhiteSpace(' ')(29,30) - JqlLiteral(40)(30,32) - PsiElement(JQL: NUMBER_LITERAL)('40')(30,32) - PsiElement(JQL: RPAR)(')')(32,33) - PsiElement(JQL: RPAR)(')')(33,34) - PsiElement(JQL: RPAR)(')')(34,35) - PsiElement(JQL: RPAR)(')')(35,36) + JqlSubClause((a = foo or (b > 12 and ((c < 40)))))(0,36) + PsiElement(JQL: LPAR)('(')(0,1) + JqlOrClause(a = foo or (b > 12 and ((c < 40))))(1,35) + JqlSimpleClause(a = foo)(1,8) + JqlIdentifier(a)(1,2) + PsiElement(JQL: STRING_LITERAL)('a')(1,2) + PsiWhiteSpace(' ')(2,3) + PsiElement(JQL: EQ)('=')(3,4) + PsiWhiteSpace(' ')(4,5) + JqlLiteral(foo)(5,8) + PsiElement(JQL: STRING_LITERAL)('foo')(5,8) + PsiWhiteSpace(' ')(8,9) + PsiElement(JQL: OR_KEYWORD)('or')(9,11) + PsiWhiteSpace(' ')(11,12) + JqlSubClause((b > 12 and ((c < 40))))(12,35) + PsiElement(JQL: LPAR)('(')(12,13) + JqlAndClause(b > 12 and ((c < 40)))(13,34) + JqlSimpleClause(b > 12)(13,19) + JqlIdentifier(b)(13,14) + PsiElement(JQL: STRING_LITERAL)('b')(13,14) + PsiWhiteSpace(' ')(14,15) + PsiElement(JQL: GT)('>')(15,16) + PsiWhiteSpace(' ')(16,17) + JqlLiteral(12)(17,19) + PsiElement(JQL: NUMBER_LITERAL)('12')(17,19) + PsiWhiteSpace(' ')(19,20) + PsiElement(JQL: AND_KEYWORD)('and')(20,23) + PsiWhiteSpace(' ')(23,24) + JqlSubClause(((c < 40)))(24,34) + PsiElement(JQL: LPAR)('(')(24,25) + JqlSubClause((c < 40))(25,33) + PsiElement(JQL: LPAR)('(')(25,26) + JqlSimpleClause(c < 40)(26,32) + JqlIdentifier(c)(26,27) + PsiElement(JQL: STRING_LITERAL)('c')(26,27) + PsiWhiteSpace(' ')(27,28) + PsiElement(JQL: LT)('<')(28,29) + PsiWhiteSpace(' ')(29,30) + JqlLiteral(40)(30,32) + PsiElement(JQL: NUMBER_LITERAL)('40')(30,32) + PsiElement(JQL: RPAR)(')')(32,33) + PsiElement(JQL: RPAR)(')')(33,34) + PsiElement(JQL: RPAR)(')')(34,35) + PsiElement(JQL: RPAR)(')')(35,36) PsiWhiteSpace(' ')(36,37) PsiElement(JQL: AND_KEYWORD)('and')(37,40) PsiWhiteSpace(' ')(40,41) diff --git a/plugins/terminal/lib/jediterm-pty-0.08.jar b/plugins/terminal/lib/jediterm-pty-0.08.jar Binary files differindex 0e3c22395e8e..f4bd38697dc2 100644 --- a/plugins/terminal/lib/jediterm-pty-0.08.jar +++ b/plugins/terminal/lib/jediterm-pty-0.08.jar diff --git a/plugins/terminal/lib/pty4j-0.3.jar b/plugins/terminal/lib/pty4j-0.3.jar Binary files differindex 67200e1203af..3c7f3db9811a 100644 --- a/plugins/terminal/lib/pty4j-0.3.jar +++ b/plugins/terminal/lib/pty4j-0.3.jar diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/AbstractTerminalRunner.java b/plugins/terminal/src/org/jetbrains/plugins/terminal/AbstractTerminalRunner.java index c1ceb923a569..fcf06272b26c 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/AbstractTerminalRunner.java +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/AbstractTerminalRunner.java @@ -1,5 +1,6 @@ package org.jetbrains.plugins.terminal; +import com.google.common.base.Predicate; import com.intellij.execution.ExecutionManager; import com.intellij.execution.Executor; import com.intellij.execution.executors.DefaultRunExecutor; @@ -8,30 +9,22 @@ import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.execution.ui.actions.CloseAction; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.keymap.KeymapManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.util.ui.UIUtil; -import com.jediterm.pty.PtyProcessTtyConnector; import com.jediterm.terminal.TtyConnector; -import com.jediterm.terminal.emulator.ColorPalette; -import com.jediterm.terminal.ui.AbstractSystemSettingsProvider; import com.jediterm.terminal.ui.TerminalSession; import com.jediterm.terminal.ui.TerminalWidget; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; -import java.awt.event.ActionEvent; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.ExecutionException; /** @@ -90,9 +83,14 @@ public abstract class AbstractTerminalRunner<T extends Process> { protected abstract ProcessHandler createProcessHandler(T process); public JBTabbedTerminalWidget createTerminalWidget() { - JBTerminalSystemSettingsProvider provider = new JBTerminalSystemSettingsProvider(); - JBTabbedTerminalWidget terminalWidget = new JBTabbedTerminalWidget(provider); - provider.setTerminalWidget(terminalWidget); + final JBTerminalSystemSettingsProvider provider = new JBTerminalSystemSettingsProvider(); + JBTabbedTerminalWidget terminalWidget = new JBTabbedTerminalWidget(provider, new Predicate<TerminalWidget>() { + @Override + public boolean apply(TerminalWidget widget) { + openSession(widget); + return true; + } + }); openSession(terminalWidget); return terminalWidget; } @@ -102,9 +100,14 @@ public abstract class AbstractTerminalRunner<T extends Process> { final DefaultActionGroup toolbarActions = new DefaultActionGroup(); final ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, toolbarActions, false); - JBTerminalSystemSettingsProvider provider = new JBTerminalSystemSettingsProvider(); - TerminalWidget widget = new JBTabbedTerminalWidget(provider); - provider.setTerminalWidget(widget); + final JBTerminalSystemSettingsProvider provider = new JBTerminalSystemSettingsProvider(); + TerminalWidget widget = new JBTabbedTerminalWidget(provider, new Predicate<TerminalWidget>() { + @Override + public boolean apply(TerminalWidget widget) { + openSession(widget); + return true; + } + }); openSession(widget, createTtyConnector(process)); @@ -160,57 +163,6 @@ public abstract class AbstractTerminalRunner<T extends Process> { return myProject; } - private class JBTerminalSystemSettingsProvider extends AbstractSystemSettingsProvider { - private TerminalWidget myTerminalWidget; - - public void setTerminalWidget(TerminalWidget terminalWidget) { - myTerminalWidget = terminalWidget; - } - - @Override - public AbstractAction getNewSessionAction() { - return new AbstractAction("New Session") { - @Override - public void actionPerformed(ActionEvent event) { - openSession(myTerminalWidget); - } - }; - } - - @Override - public KeyStroke[] getCopyKeyStrokes() { - return getKeyStrokesByActionId("$Copy"); - } - - @Override - public KeyStroke[] getPasteKeyStrokes() { - return getKeyStrokesByActionId("$Paste"); - } - - @Override - public ColorPalette getPalette() { - return SystemInfo.isWindows ? ColorPalette.WINDOWS_PALETTE : ColorPalette.XTERM_PALETTE; - } - - private KeyStroke[] getKeyStrokesByActionId(String actionId) { - List<KeyStroke> keyStrokes = new ArrayList<KeyStroke>(); - Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts(actionId); - for (Shortcut sc : shortcuts) { - if (sc instanceof KeyboardShortcut) { - KeyStroke ks = ((KeyboardShortcut)sc).getFirstKeyStroke(); - keyStrokes.add(ks); - } - } - - return keyStrokes.toArray(new KeyStroke[keyStrokes.size()]); - } - - @Override - public boolean shouldCloseTabOnLogout(TtyConnector ttyConnector) { - return ttyConnector instanceof PtyProcessTtyConnector; //close tab only on logout of local pty, not remote - } - } - public void openSession(TerminalWidget terminalWidget) { // Create Server process try { diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/JBTabbedTerminalWidget.java b/plugins/terminal/src/org/jetbrains/plugins/terminal/JBTabbedTerminalWidget.java index 051a5a54359d..50a7e41ceb71 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/JBTabbedTerminalWidget.java +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/JBTabbedTerminalWidget.java @@ -1,12 +1,14 @@ package org.jetbrains.plugins.terminal; +import com.google.common.base.Predicate; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.DumbAwareAction; import com.jediterm.terminal.ui.JediTermWidget; -import com.jediterm.terminal.ui.SystemSettingsProvider; import com.jediterm.terminal.ui.TabbedTerminalWidget; import com.jediterm.terminal.ui.TerminalAction; +import com.jediterm.terminal.ui.TerminalWidget; +import com.jediterm.terminal.ui.settings.SettingsProvider; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -17,8 +19,9 @@ import java.util.List; * @author traff */ public class JBTabbedTerminalWidget extends TabbedTerminalWidget { - public JBTabbedTerminalWidget(@NotNull SystemSettingsProvider settingsProvider) { - super(settingsProvider); + + public JBTabbedTerminalWidget(@NotNull SettingsProvider settingsProvider, @NotNull Predicate<TerminalWidget> createNewSessionAction) { + super(settingsProvider, createNewSessionAction); convertActions(this, getActions()); } @@ -28,12 +31,7 @@ public class JBTabbedTerminalWidget extends TabbedTerminalWidget { AnAction a = new DumbAwareAction() { @Override public void actionPerformed(AnActionEvent e) { - if (e.getInputEvent() instanceof KeyEvent) { - action.perform((KeyEvent)e.getInputEvent()); - } - else { - action.perform(null); - } + action.perform(e.getInputEvent() instanceof KeyEvent? (KeyEvent)e.getInputEvent() : null); } }; a.registerCustomShortcutSet(action.getKeyCode(), action.getModifiers(), component); @@ -41,7 +39,7 @@ public class JBTabbedTerminalWidget extends TabbedTerminalWidget { } @Override - protected JediTermWidget createInnerTerminalWidget() { - return new JBTerminalWidget(getSystemSettingsProvider()); + protected JediTermWidget createInnerTerminalWidget(SettingsProvider settingsProvider) { + return new JBTerminalWidget(settingsProvider); } } diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/JBTerminalPanel.java b/plugins/terminal/src/org/jetbrains/plugins/terminal/JBTerminalPanel.java index e5b6e533e705..788a4f960e85 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/JBTerminalPanel.java +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/JBTerminalPanel.java @@ -22,17 +22,14 @@ package org.jetbrains.plugins.terminal; -import com.intellij.openapi.editor.colors.EditorColors; -import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.ide.CopyPasteManager; +import com.intellij.util.JBHiDPIScaledImage; import com.intellij.util.ui.GraphicsUtil; import com.intellij.util.ui.UIUtil; -import com.jediterm.terminal.TerminalColor; -import com.jediterm.terminal.TextStyle; import com.jediterm.terminal.display.BackBuffer; import com.jediterm.terminal.display.StyleState; -import com.jediterm.terminal.ui.SystemSettingsProvider; import com.jediterm.terminal.ui.TerminalPanel; +import com.jediterm.terminal.ui.settings.SettingsProvider; import org.jetbrains.annotations.NotNull; import java.awt.*; @@ -41,69 +38,57 @@ import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.image.BufferedImage; +import java.awt.image.ImageObserver; import java.io.IOException; -import java.util.List; public class JBTerminalPanel extends TerminalPanel { - private final EditorColorsScheme myColorScheme; - - public JBTerminalPanel(@NotNull SystemSettingsProvider settingsProvider, + public JBTerminalPanel(@NotNull SettingsProvider settingsProvider, @NotNull BackBuffer backBuffer, - @NotNull StyleState styleState, - @NotNull EditorColorsScheme scheme) { + @NotNull StyleState styleState) { super(settingsProvider, backBuffer, styleState); - myColorScheme = scheme; - - - styleState.setDefaultStyle(new TextStyle(TerminalColor.awt(myColorScheme.getDefaultForeground()), TerminalColor.awt( - myColorScheme.getDefaultBackground()))); - - setSelectionColor(new TextStyle(TerminalColor.awt(myColorScheme.getColor(EditorColors.SELECTION_FOREGROUND_COLOR)), - TerminalColor.awt(myColorScheme.getColor(EditorColors.SELECTION_BACKGROUND_COLOR)))); - setLineSpace(myColorScheme.getConsoleLineSpacing()); - JBTabbedTerminalWidget.convertActions(this, getActions()); } - protected Font createFont() { - - Font normalFont = Font.decode(getFontName()); - - if (normalFont == null) { - normalFont = super.createFont(); - } - - normalFont = normalFont.deriveFont((float)myColorScheme.getConsoleFontSize()); - - return normalFont; - } - protected void setupAntialiasing(Graphics graphics, boolean antialiasing) { GraphicsUtil.setupAntialiasing(graphics, antialiasing, false); } @Override - public Color getBackground() { - if (myColorScheme != null) { - return myColorScheme.getDefaultBackground(); - } - return super.getBackground(); + protected void setCopyContents(StringSelection selection) { + CopyPasteManager.getInstance().setContents(selection); } @Override - public Color getForeground() { - if (myColorScheme != null) { - return myColorScheme.getDefaultForeground(); - } - return super.getBackground(); + protected void drawImage(Graphics2D gfx, BufferedImage image, int x, int y, ImageObserver observer) { + UIUtil.drawImage(gfx, image, x, y, observer); } @Override - protected void setCopyContents(StringSelection selection) { - CopyPasteManager.getInstance().setContents(selection); + protected void drawImage(Graphics2D g, BufferedImage image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2) { + drawImage(g, image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null); + } + + public static void drawImage(Graphics g, Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { + if (image instanceof JBHiDPIScaledImage) { + final Graphics2D newG = (Graphics2D)g.create(0, 0, image.getWidth(observer), image.getHeight(observer)); + newG.scale(0.5, 0.5); + Image img = ((JBHiDPIScaledImage)image).getDelegate(); + if (img == null) { + img = image; + } + newG.drawImage(img, 2*dx1, 2*dy1, 2*dx2, 2*dy2, sx1, sy1, sx2, sy2, observer); + newG.scale(1, 1); + newG.dispose(); + } else { + g.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); + } } + @Override + protected boolean isRetina() { + return UIUtil.isRetina(); + } @Override protected String getClipboardContent() throws IOException, UnsupportedFlavorException { @@ -118,28 +103,5 @@ public class JBTerminalPanel extends TerminalPanel { protected BufferedImage createBufferedImage(int width, int height) { return UIUtil.createImage(width, height, BufferedImage.TYPE_INT_ARGB); } - - @Override - protected void drawImage(Graphics2D g, BufferedImage image) { - UIUtil.drawImage(g, image, null, 0, 0); - } - - public String getFontName() { - List<String> fonts = myColorScheme.getConsoleFontPreferences().getEffectiveFontFamilies(); - - for (String font : fonts) { - if (isApplicable(font)) { - return font; - } - } - return super.getFontName(); - } - - private static boolean isApplicable(String font) { - if ("Source Code Pro".equals(font)) { - return false; - } - return true; - } } diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/JBTerminalSystemSettingsProvider.java b/plugins/terminal/src/org/jetbrains/plugins/terminal/JBTerminalSystemSettingsProvider.java new file mode 100644 index 000000000000..3b983e21410f --- /dev/null +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/JBTerminalSystemSettingsProvider.java @@ -0,0 +1,367 @@ +package org.jetbrains.plugins.terminal; + +import com.intellij.application.options.OptionsConstants; +import com.intellij.openapi.actionSystem.KeyboardShortcut; +import com.intellij.openapi.actionSystem.Shortcut; +import com.intellij.openapi.editor.colors.*; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.keymap.KeymapManager; +import com.intellij.openapi.options.FontSize; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.util.containers.HashMap; +import com.jediterm.pty.PtyProcessTtyConnector; +import com.jediterm.terminal.TerminalColor; +import com.jediterm.terminal.TextStyle; +import com.jediterm.terminal.TtyConnector; +import com.jediterm.terminal.emulator.ColorPalette; +import com.jediterm.terminal.ui.settings.DefaultSettingsProvider; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +/** + * @author traff + */ +class JBTerminalSystemSettingsProvider extends DefaultSettingsProvider { + + private final EditorColorsScheme myColorScheme; + + JBTerminalSystemSettingsProvider() { + myColorScheme = getColorScheme(); + } + + @Override + public KeyStroke[] getCopyKeyStrokes() { + return getKeyStrokesByActionId("$Copy"); + } + + @Override + public KeyStroke[] getPasteKeyStrokes() { + return getKeyStrokesByActionId("$Paste"); + } + + @Override + public ColorPalette getTerminalColorPalette() { + return SystemInfo.isWindows ? ColorPalette.WINDOWS_PALETTE : ColorPalette.XTERM_PALETTE; + } + + private KeyStroke[] getKeyStrokesByActionId(String actionId) { + List<KeyStroke> keyStrokes = new ArrayList<KeyStroke>(); + Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts(actionId); + for (Shortcut sc : shortcuts) { + if (sc instanceof KeyboardShortcut) { + KeyStroke ks = ((KeyboardShortcut)sc).getFirstKeyStroke(); + keyStrokes.add(ks); + } + } + + return keyStrokes.toArray(new KeyStroke[keyStrokes.size()]); + } + + @Override + public boolean shouldCloseTabOnLogout(TtyConnector ttyConnector) { + return ttyConnector instanceof PtyProcessTtyConnector; //close tab only on logout of local pty, not remote + } + + @Override + public float getLineSpace() { + return myColorScheme.getConsoleLineSpacing(); + } + + @Override + public boolean useInverseSelectionColor() { + return false; + } + + @Override + public TextStyle getSelectionColor() { + return new TextStyle(TerminalColor.awt(myColorScheme.getColor(EditorColors.SELECTION_FOREGROUND_COLOR)), + TerminalColor.awt(myColorScheme.getColor(EditorColors.SELECTION_BACKGROUND_COLOR))); + } + + @Override + public TextStyle getDefaultStyle() { + return new TextStyle(TerminalColor.awt(myColorScheme.getDefaultForeground()), TerminalColor.awt( + myColorScheme.getDefaultBackground())); + } + + @Override + public Font getTerminalFont() { + Font normalFont = Font.decode(getFontName()); + + if (normalFont == null) { + normalFont = super.getTerminalFont(); + } + + normalFont = normalFont.deriveFont(getTerminalFontSize()); + + return normalFont; + } + + public String getFontName() { + List<String> fonts = myColorScheme.getConsoleFontPreferences().getEffectiveFontFamilies(); + + for (String font : fonts) { + if (isApplicable(font)) { + return font; + } + } + return "Monospaced-14"; + } + + private static boolean isApplicable(String font) { + if ("Source Code Pro".equals(font)) { + return false; + } + return true; + } + + @Override + public float getTerminalFontSize() { + return (float)myColorScheme.getConsoleFontSize(); + } + + public EditorColorsScheme getColorScheme() { + return createBoundColorSchemeDelegate(null); + } + + @NotNull + public EditorColorsScheme createBoundColorSchemeDelegate(@Nullable final EditorColorsScheme customGlobalScheme) { + return new MyColorSchemeDelegate(customGlobalScheme); + } + + private static class MyColorSchemeDelegate implements EditorColorsScheme { + + private final FontPreferences myFontPreferences = new FontPreferences(); + private final HashMap<TextAttributesKey, TextAttributes> myOwnAttributes = new HashMap<TextAttributesKey, TextAttributes>(); + private final HashMap<ColorKey, Color> myOwnColors = new HashMap<ColorKey, Color>(); + private final EditorColorsScheme myCustomGlobalScheme; + private Map<EditorFontType, Font> myFontsMap = null; + private int myMaxFontSize = OptionsConstants.MAX_EDITOR_FONT_SIZE; + private int myFontSize = -1; + private String myFaceName = null; + private EditorColorsScheme myGlobalScheme; + + private MyColorSchemeDelegate(@Nullable final EditorColorsScheme globalScheme) { + myCustomGlobalScheme = globalScheme; + updateGlobalScheme(); + } + + private EditorColorsScheme getGlobal() { + return myGlobalScheme; + } + + @Override + public String getName() { + return getGlobal().getName(); + } + + + protected void initFonts() { + String editorFontName = getEditorFontName(); + int editorFontSize = getEditorFontSize(); + myFontPreferences.clear(); + myFontPreferences.register(editorFontName, editorFontSize); + + myFontsMap = new EnumMap<EditorFontType, Font>(EditorFontType.class); + + Font plainFont = new Font(editorFontName, Font.PLAIN, editorFontSize); + Font boldFont = new Font(editorFontName, Font.BOLD, editorFontSize); + Font italicFont = new Font(editorFontName, Font.ITALIC, editorFontSize); + Font boldItalicFont = new Font(editorFontName, Font.BOLD | Font.ITALIC, editorFontSize); + + myFontsMap.put(EditorFontType.PLAIN, plainFont); + myFontsMap.put(EditorFontType.BOLD, boldFont); + myFontsMap.put(EditorFontType.ITALIC, italicFont); + myFontsMap.put(EditorFontType.BOLD_ITALIC, boldItalicFont); + } + + @Override + public void setName(String name) { + getGlobal().setName(name); + } + + @Override + public TextAttributes getAttributes(TextAttributesKey key) { + if (myOwnAttributes.containsKey(key)) return myOwnAttributes.get(key); + return getGlobal().getAttributes(key); + } + + @Override + public void setAttributes(TextAttributesKey key, TextAttributes attributes) { + myOwnAttributes.put(key, attributes); + } + + @NotNull + @Override + public Color getDefaultBackground() { + return getGlobal().getDefaultBackground(); + } + + @NotNull + @Override + public Color getDefaultForeground() { + return getGlobal().getDefaultForeground(); + } + + @Override + public Color getColor(ColorKey key) { + if (myOwnColors.containsKey(key)) return myOwnColors.get(key); + return getGlobal().getColor(key); + } + + @Override + public void setColor(ColorKey key, Color color) { + myOwnColors.put(key, color); + } + + @NotNull + @Override + public FontPreferences getFontPreferences() { + return myFontPreferences; + } + + @Override + public void setFontPreferences(@NotNull FontPreferences preferences) { + preferences.copyTo(myFontPreferences); + initFonts(); + } + + @Override + public int getEditorFontSize() { + if (myFontSize == -1) { + return getGlobal().getEditorFontSize(); + } + return myFontSize; + } + + @Override + public void setEditorFontSize(int fontSize) { + if (fontSize < 8) fontSize = 8; + if (fontSize > myMaxFontSize) fontSize = myMaxFontSize; + myFontSize = fontSize; + initFonts(); + } + + @Override + public FontSize getQuickDocFontSize() { + return myGlobalScheme.getQuickDocFontSize(); + } + + @Override + public void setQuickDocFontSize(@NotNull FontSize fontSize) { + myGlobalScheme.setQuickDocFontSize(fontSize); + } + + @Override + public String getEditorFontName() { + if (myFaceName == null) { + return getGlobal().getEditorFontName(); + } + return myFaceName; + } + + @Override + public void setEditorFontName(String fontName) { + myFaceName = fontName; + initFonts(); + } + + @Override + public Font getFont(EditorFontType key) { + if (myFontsMap != null) { + Font font = myFontsMap.get(key); + if (font != null) return font; + } + return getGlobal().getFont(key); + } + + @Override + public void setFont(EditorFontType key, Font font) { + if (myFontsMap == null) { + initFonts(); + } + myFontsMap.put(key, font); + } + + @Override + public float getLineSpacing() { + return getGlobal().getLineSpacing(); + } + + @Override + public void setLineSpacing(float lineSpacing) { + getGlobal().setLineSpacing(lineSpacing); + } + + @Override + @Nullable + public Object clone() { + return null; + } + + @Override + public void readExternal(Element element) throws InvalidDataException { + } + + @Override + public void writeExternal(Element element) throws WriteExternalException { + } + + public void updateGlobalScheme() { + myGlobalScheme = myCustomGlobalScheme == null ? EditorColorsManager.getInstance().getGlobalScheme() : myCustomGlobalScheme; + int globalFontSize = getGlobal().getEditorFontSize(); + myMaxFontSize = Math.max(OptionsConstants.MAX_EDITOR_FONT_SIZE, globalFontSize); + } + + @NotNull + @Override + public FontPreferences getConsoleFontPreferences() { + return getGlobal().getConsoleFontPreferences(); + } + + @Override + public void setConsoleFontPreferences(@NotNull FontPreferences preferences) { + getGlobal().setConsoleFontPreferences(preferences); + } + + @Override + public String getConsoleFontName() { + return getGlobal().getConsoleFontName(); + } + + @Override + public void setConsoleFontName(String fontName) { + getGlobal().setConsoleFontName(fontName); + } + + @Override + public int getConsoleFontSize() { + return getGlobal().getConsoleFontSize(); + } + + @Override + public void setConsoleFontSize(int fontSize) { + getGlobal().setConsoleFontSize(fontSize); + } + + @Override + public float getConsoleLineSpacing() { + return getGlobal().getConsoleLineSpacing(); + } + + @Override + public void setConsoleLineSpacing(float lineSpacing) { + getGlobal().setConsoleLineSpacing(lineSpacing); + } + } +} diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/JBTerminalWidget.java b/plugins/terminal/src/org/jetbrains/plugins/terminal/JBTerminalWidget.java index 6f2e49da3f27..99a21e868034 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/JBTerminalWidget.java +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/JBTerminalWidget.java @@ -14,7 +14,7 @@ import com.jediterm.terminal.display.BackBuffer; import com.jediterm.terminal.display.JediTerminal; import com.jediterm.terminal.display.StyleState; import com.jediterm.terminal.ui.JediTermWidget; -import com.jediterm.terminal.ui.SystemSettingsProvider; +import com.jediterm.terminal.ui.settings.SettingsProvider; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,17 +26,17 @@ import java.util.Map; public class JBTerminalWidget extends JediTermWidget { - public JBTerminalWidget(SystemSettingsProvider settingsProvider) { + public JBTerminalWidget(SettingsProvider settingsProvider) { super(settingsProvider); JBTabbedTerminalWidget.convertActions(this, getActions()); } @Override - protected JBTerminalPanel createTerminalPanel(@NotNull SystemSettingsProvider settingsProvider, + protected JBTerminalPanel createTerminalPanel(@NotNull SettingsProvider settingsProvider, @NotNull StyleState styleState, @NotNull BackBuffer backBuffer) { - return new JBTerminalPanel(settingsProvider, backBuffer, styleState, getColorScheme()); + return new JBTerminalPanel(settingsProvider, backBuffer, styleState); } @Override @@ -44,243 +44,10 @@ public class JBTerminalWidget extends JediTermWidget { return new JBTerminalStarter(terminal, connector); } - public EditorColorsScheme getColorScheme() { - return createBoundColorSchemeDelegate(null); - } - @Override protected JScrollBar createScrollBar() { return new JBScrollBar(); } - @NotNull - public EditorColorsScheme createBoundColorSchemeDelegate(@Nullable final EditorColorsScheme customGlobalScheme) { - return new MyColorSchemeDelegate(customGlobalScheme); - } - - private static class MyColorSchemeDelegate implements EditorColorsScheme { - - private final FontPreferences myFontPreferences = new FontPreferences(); - private final HashMap<TextAttributesKey, TextAttributes> myOwnAttributes = new HashMap<TextAttributesKey, TextAttributes>(); - private final HashMap<ColorKey, Color> myOwnColors = new HashMap<ColorKey, Color>(); - private final EditorColorsScheme myCustomGlobalScheme; - private Map<EditorFontType, Font> myFontsMap = null; - private int myMaxFontSize = OptionsConstants.MAX_EDITOR_FONT_SIZE; - private int myFontSize = -1; - private String myFaceName = null; - private EditorColorsScheme myGlobalScheme; - - private MyColorSchemeDelegate(@Nullable final EditorColorsScheme globalScheme) { - myCustomGlobalScheme = globalScheme; - updateGlobalScheme(); - } - - private EditorColorsScheme getGlobal() { - return myGlobalScheme; - } - - @Override - public String getName() { - return getGlobal().getName(); - } - - - protected void initFonts() { - String editorFontName = getEditorFontName(); - int editorFontSize = getEditorFontSize(); - myFontPreferences.clear(); - myFontPreferences.register(editorFontName, editorFontSize); - - myFontsMap = new EnumMap<EditorFontType, Font>(EditorFontType.class); - - Font plainFont = new Font(editorFontName, Font.PLAIN, editorFontSize); - Font boldFont = new Font(editorFontName, Font.BOLD, editorFontSize); - Font italicFont = new Font(editorFontName, Font.ITALIC, editorFontSize); - Font boldItalicFont = new Font(editorFontName, Font.BOLD | Font.ITALIC, editorFontSize); - - myFontsMap.put(EditorFontType.PLAIN, plainFont); - myFontsMap.put(EditorFontType.BOLD, boldFont); - myFontsMap.put(EditorFontType.ITALIC, italicFont); - myFontsMap.put(EditorFontType.BOLD_ITALIC, boldItalicFont); - } - - @Override - public void setName(String name) { - getGlobal().setName(name); - } - - @Override - public TextAttributes getAttributes(TextAttributesKey key) { - if (myOwnAttributes.containsKey(key)) return myOwnAttributes.get(key); - return getGlobal().getAttributes(key); - } - - @Override - public void setAttributes(TextAttributesKey key, TextAttributes attributes) { - myOwnAttributes.put(key, attributes); - } - - @NotNull - @Override - public Color getDefaultBackground() { - return getGlobal().getDefaultBackground(); - } - - @NotNull - @Override - public Color getDefaultForeground() { - return getGlobal().getDefaultForeground(); - } - - @Override - public Color getColor(ColorKey key) { - if (myOwnColors.containsKey(key)) return myOwnColors.get(key); - return getGlobal().getColor(key); - } - - @Override - public void setColor(ColorKey key, Color color) { - myOwnColors.put(key, color); - } - - @NotNull - @Override - public FontPreferences getFontPreferences() { - return myFontPreferences; - } - - @Override - public void setFontPreferences(@NotNull FontPreferences preferences) { - preferences.copyTo(myFontPreferences); - initFonts(); - } - @Override - public int getEditorFontSize() { - if (myFontSize == -1) { - return getGlobal().getEditorFontSize(); - } - return myFontSize; - } - - @Override - public void setEditorFontSize(int fontSize) { - if (fontSize < 8) fontSize = 8; - if (fontSize > myMaxFontSize) fontSize = myMaxFontSize; - myFontSize = fontSize; - initFonts(); - } - - @Override - public FontSize getQuickDocFontSize() { - return myGlobalScheme.getQuickDocFontSize(); - } - - @Override - public void setQuickDocFontSize(@NotNull FontSize fontSize) { - myGlobalScheme.setQuickDocFontSize(fontSize); - } - - @Override - public String getEditorFontName() { - if (myFaceName == null) { - return getGlobal().getEditorFontName(); - } - return myFaceName; - } - - @Override - public void setEditorFontName(String fontName) { - myFaceName = fontName; - initFonts(); - } - - @Override - public Font getFont(EditorFontType key) { - if (myFontsMap != null) { - Font font = myFontsMap.get(key); - if (font != null) return font; - } - return getGlobal().getFont(key); - } - - @Override - public void setFont(EditorFontType key, Font font) { - if (myFontsMap == null) { - initFonts(); - } - myFontsMap.put(key, font); - } - - @Override - public float getLineSpacing() { - return getGlobal().getLineSpacing(); - } - - @Override - public void setLineSpacing(float lineSpacing) { - getGlobal().setLineSpacing(lineSpacing); - } - - @Override - @Nullable - public Object clone() { - return null; - } - - @Override - public void readExternal(Element element) throws InvalidDataException { - } - - @Override - public void writeExternal(Element element) throws WriteExternalException { - } - - public void updateGlobalScheme() { - myGlobalScheme = myCustomGlobalScheme == null ? EditorColorsManager.getInstance().getGlobalScheme() : myCustomGlobalScheme; - int globalFontSize = getGlobal().getEditorFontSize(); - myMaxFontSize = Math.max(OptionsConstants.MAX_EDITOR_FONT_SIZE, globalFontSize); - } - - @NotNull - @Override - public FontPreferences getConsoleFontPreferences() { - return getGlobal().getConsoleFontPreferences(); - } - - @Override - public void setConsoleFontPreferences(@NotNull FontPreferences preferences) { - getGlobal().setConsoleFontPreferences(preferences); - } - - @Override - public String getConsoleFontName() { - return getGlobal().getConsoleFontName(); - } - - @Override - public void setConsoleFontName(String fontName) { - getGlobal().setConsoleFontName(fontName); - } - - @Override - public int getConsoleFontSize() { - return getGlobal().getConsoleFontSize(); - } - - @Override - public void setConsoleFontSize(int fontSize) { - getGlobal().setConsoleFontSize(fontSize); - } - - @Override - public float getConsoleLineSpacing() { - return getGlobal().getConsoleLineSpacing(); - } - - @Override - public void setConsoleLineSpacing(float lineSpacing) { - getGlobal().setConsoleLineSpacing(lineSpacing); - } - } } diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/OpenLocalTerminalAction.java b/plugins/terminal/src/org/jetbrains/plugins/terminal/OpenLocalTerminalAction.java index 203de242cb6e..8cb28eaa74f3 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/OpenLocalTerminalAction.java +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/OpenLocalTerminalAction.java @@ -21,9 +21,8 @@ public class OpenLocalTerminalAction extends AnAction implements DumbAware { @Override public void update(final AnActionEvent e) { - boolean enabled = SystemInfo.isUnix; - e.getPresentation().setVisible(enabled); - e.getPresentation().setEnabled(enabled); + e.getPresentation().setVisible(true); + e.getPresentation().setEnabled(true); } public void actionPerformed(final AnActionEvent e) { diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalToolWindowFactory.java b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalToolWindowFactory.java index 7d57aacd75bb..271c8dcb42fc 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalToolWindowFactory.java +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalToolWindowFactory.java @@ -9,6 +9,8 @@ import com.intellij.openapi.wm.ToolWindowFactory; * @author traff */ public class TerminalToolWindowFactory implements ToolWindowFactory, DumbAware { + public static final String TOOL_WINDOW_ID = "Terminal"; + @Override public void createToolWindowContent(Project project, ToolWindow toolWindow) { TerminalView terminalView = TerminalView.getInstance(); diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalView.java b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalView.java index bd7cfd23675e..188be8f15d48 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalView.java +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalView.java @@ -9,15 +9,20 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.SimpleToolWindowPanel; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.openapi.wm.ex.ToolWindowManagerEx; +import com.intellij.openapi.wm.ex.ToolWindowManagerListener; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentFactory; +import com.intellij.util.ui.UIUtil; import com.jediterm.terminal.ui.JediTermWidget; import com.jediterm.terminal.ui.TabbedTerminalWidget; import com.jediterm.terminal.ui.TerminalWidget; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.awt.*; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; @@ -29,25 +34,47 @@ public class TerminalView { private JBTabbedTerminalWidget myTerminalWidget; private Project myProject; - public void initTerminal(Project project, final ToolWindow toolWindow) { + public void initTerminal(final Project project, final ToolWindow toolWindow) { myProject = project; LocalTerminalDirectRunner terminalRunner = OpenLocalTerminalAction.createTerminalRunner(project); - + toolWindow.setToHideOnEmptyContent(true); - + if (terminalRunner != null) { myTerminalWidget = terminalRunner.createTerminalWidget(); myTerminalWidget.addTabListener(new TabbedTerminalWidget.TabListener() { @Override public void tabClosed(JediTermWidget terminal) { - hideIfNoActiveSessions(toolWindow, myTerminalWidget); + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + hideIfNoActiveSessions(toolWindow, myTerminalWidget); + } + }); } }); } Content content = createToolWindowContentPanel(terminalRunner, myTerminalWidget, toolWindow); - + toolWindow.getContentManager().addContent(content); + + ((ToolWindowManagerEx)ToolWindowManager.getInstance(myProject)).addToolWindowManagerListener(new ToolWindowManagerListener() { + @Override + public void toolWindowRegistered(@NotNull String id) { + } + + @Override + public void stateChanged() { + ToolWindow window = ToolWindowManager.getInstance(project).getToolWindow(TerminalToolWindowFactory.TOOL_WINDOW_ID); + if (window != null) { + boolean visible = window.isVisible(); + if (visible && toolWindow.getContentManager().getContentCount() == 0) { + initTerminal(project, window); + } + } + } + }); } private Content createToolWindowContentPanel(@Nullable LocalTerminalDirectRunner terminalRunner, @@ -59,6 +86,7 @@ public class TerminalView { return PlatformDataKeys.HELP_ID.is(dataId) ? EventLog.HELP_ID : super.getData(dataId); } }; + if (terminalWidget != null) { panel.setContent(terminalWidget.getComponent()); @@ -73,9 +101,8 @@ public class TerminalView { final Content content = ContentFactory.SERVICE.getInstance().createContent(panel, "", false); content.setCloseable(true); - if (getComponentToFocus() != null) { - content.setPreferredFocusableComponent(getComponentToFocus()); - } + content.setPreferredFocusableComponent(terminalWidget.getComponent()); + return content; } @@ -129,7 +156,7 @@ public class TerminalView { } private ActionToolbar createToolbar(@Nullable final LocalTerminalDirectRunner terminalRunner, - final JBTabbedTerminalWidget terminal, ToolWindow toolWindow) { + final JBTabbedTerminalWidget terminal, ToolWindow toolWindow) { DefaultActionGroup group = new DefaultActionGroup(); if (terminalRunner != null) { @@ -151,16 +178,9 @@ public class TerminalView { }, true); } - private void hideIfNoActiveSessions(final ToolWindow toolWindow, JBTabbedTerminalWidget terminal) { + private static void hideIfNoActiveSessions(final ToolWindow toolWindow, JBTabbedTerminalWidget terminal) { if (terminal.isNoActiveSessions()) { toolWindow.getContentManager().removeAllContents(true); - - toolWindow.getActivation().doWhenDone(new Runnable() { - @Override - public void run() { - initTerminal(myProject, toolWindow); - } - }); } } diff --git a/plugins/terminal/terminal.iml b/plugins/terminal/terminal.iml index 86f90598c51f..59daabb61b81 100644 --- a/plugins/terminal/terminal.iml +++ b/plugins/terminal/terminal.iml @@ -48,6 +48,7 @@ <SOURCES /> </library> </orderEntry> + <orderEntry type="library" name="Guava" level="project" /> </component> </module> diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestData.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestData.java index f88daee3e06b..55a6a3db11b5 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/model/TestData.java +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestData.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -177,7 +177,7 @@ public class TestData implements Cloneable public String getGeneratedName(JavaRunConfigurationModule runconfigurationmodule) { if (TestType.PACKAGE.getType().equals(TEST_OBJECT)) if (getPackageName().length() == 0) return "<default>"; else return getPackageName(); - String name = JavaExecutionUtil.getPresentableClassName(getMainClassName(), runconfigurationmodule); + String name = JavaExecutionUtil.getPresentableClassName(getMainClassName()); if (TestType.METHOD.getType().equals(TEST_OBJECT)) { return name + '.' + getMethodName(); } |