OSDN Git Service

IDisposableなオブジェクトの破棄をより厳格に行う (CA2000)
[opentween/open-tween.git] / OpenTween / MediaItem.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2015 spx (@5px)
3 // All rights reserved.
4 // 
5 // This file is part of OpenTween.
6 // 
7 // This program is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by the Free
9 // Software Foundation; either version 3 of the License, or (at your option)
10 // any later version.
11 // 
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 // for more details. 
16 // 
17 // You should have received a copy of the GNU General Public License along
18 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
19 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 // Boston, MA 02110-1301, USA.
21
22 using System;
23 using System.Drawing;
24 using System.IO;
25 using System.Linq;
26 using System.Threading;
27
28 namespace OpenTween
29 {
30     public interface IMediaItem
31     {
32         /// <summary>
33         /// メディアへの絶対パス
34         /// </summary>
35         string Path { get; }
36
37         /// <summary>
38         /// メディア名
39         /// </summary>
40         string Name { get; }
41
42         /// <summary>
43         /// メディアの拡張子
44         /// </summary>
45         string Extension { get; }
46
47         /// <summary>
48         /// メディアが存在するかどうかを示す真偽値
49         /// </summary>
50         bool Exists { get; }
51
52         /// <summary>
53         /// メディアのサイズ(バイト単位)
54         /// </summary>
55         long Size { get; }
56
57         /// <summary>
58         /// メディアが画像であるかどうかを示す真偽値
59         /// </summary>
60         bool IsImage { get; }
61
62         /// <summary>
63         /// 表示用の MemoryImage を作成する
64         /// </summary>
65         /// <remarks>
66         /// 呼び出し側にて破棄すること
67         /// </remarks>
68         MemoryImage CreateImage();
69
70         /// <summary>
71         /// メディアの内容を読み込むための Stream を開く
72         /// </summary>
73         /// <remarks>
74         /// 呼び出し側にて閉じること
75         /// </remarks>
76         Stream OpenRead();
77
78         /// <summary>
79         /// メディアの内容を Stream へ書き込む
80         /// </summary>
81         void CopyTo(Stream stream);
82     }
83
84     /// <summary>
85     /// ファイル用の MediaItem クラス
86     /// </summary>
87     public class FileMediaItem : IMediaItem
88     {
89         public FileInfo FileInfo { get; }
90
91         public FileMediaItem(string path)
92         {
93             this.FileInfo = new FileInfo(path);
94         }
95
96         public FileMediaItem(FileInfo fileInfo)
97             : this(fileInfo.FullName)
98         {
99         }
100
101         public string Path
102         {
103             get { return this.FileInfo.FullName; }
104         }
105
106         public string Name
107         {
108             get { return this.FileInfo.Name; }
109         }
110
111         public string Extension
112         {
113             get { return this.FileInfo.Extension; }
114         }
115
116         public bool Exists
117         {
118             get { return this.FileInfo.Exists; }
119         }
120
121         public long Size
122         {
123             get { return this.FileInfo.Length; }
124         }
125
126         public bool IsImage
127         {
128             get
129             {
130                 if (this.isImage == null)
131                 {
132                     try
133                     {
134                         // MemoryImage が生成できるかを検証する
135                         using (var image = this.CreateImage()) { }
136
137                         this.isImage = true;
138                     }
139                     catch (InvalidImageException)
140                     {
141                         this.isImage = false;
142                     }
143                 }
144
145                 return this.isImage.Value;
146             }
147         }
148
149         /// <summary>IsImage の検証結果をキャッシュする。未検証なら null</summary>
150         private bool? isImage = null;
151
152         public MemoryImage CreateImage()
153         {
154             using (var fs = this.FileInfo.OpenRead())
155             {
156                 return MemoryImage.CopyFromStream(fs);
157             }
158         }
159
160         public Stream OpenRead()
161         {
162             return this.FileInfo.OpenRead();
163         }
164
165         public void CopyTo(Stream stream)
166         {
167             using (var fs = this.FileInfo.OpenRead())
168             {
169                 fs.CopyTo(stream);
170             }
171         }
172     }
173
174     /// <summary>
175     /// MemoryImage 用の MediaItem クラス
176     /// </summary>
177     /// <remarks>
178     /// 用途の関係上、メモリ使用量が大きくなるため、不要になればできるだけ破棄すること
179     /// </remarks>
180     public class MemoryImageMediaItem : IMediaItem, IDisposable
181     {
182         public const string PathPrefix = "<>MemoryImage://";
183         private static int _fileNumber = 0;
184
185         private bool _disposed = false;
186
187         private readonly MemoryImage _image;
188
189         public MemoryImageMediaItem(MemoryImage image)
190         {
191             if (image == null)
192                 throw new ArgumentNullException(nameof(image));
193
194             this._image = image;
195
196             var num = Interlocked.Increment(ref _fileNumber);
197             this.Path = PathPrefix + num + this._image.ImageFormatExt;
198         }
199
200         public string Path { get; }
201
202         public string Name
203         {
204             get { return this.Path.Substring(PathPrefix.Length); }
205         }
206
207         public string Extension
208         {
209             get { return this._image.ImageFormatExt; }
210         }
211
212         public bool Exists
213         {
214             get { return this._image != null; }
215         }
216
217         public long Size
218         {
219             get { return this._image.Stream.Length; }
220         }
221
222         public bool IsImage
223         {
224             get { return true; }
225         }
226
227         public bool IsDisposed
228         {
229             get { return this._disposed; }
230         }
231
232         public MemoryImage CreateImage()
233         {
234             return this._image.Clone();
235         }
236
237         public Stream OpenRead()
238         {
239             MemoryStream memstream = null;
240             try
241             {
242                 // コピーを作成する
243                 memstream = new MemoryStream();
244
245                 this._image.Stream.WriteTo(memstream);
246                 memstream.Seek(0, SeekOrigin.Begin);
247
248                 return memstream;
249             }
250             catch
251             {
252                 memstream?.Dispose();
253                 throw;
254             }
255         }
256
257         public void CopyTo(Stream stream)
258         {
259             this._image.Stream.WriteTo(stream);
260         }
261
262         protected virtual void Dispose(bool disposing)
263         {
264             if (this._disposed) return;
265
266             if (disposing)
267             {
268                 this._image.Dispose();
269             }
270
271             this._disposed = true;
272         }
273
274         public void Dispose()
275         {
276             this.Dispose(true);
277
278             // 明示的にDisposeが呼ばれた場合はファイナライザを使用しない
279             GC.SuppressFinalize(this);
280         }
281
282         ~MemoryImageMediaItem()
283         {
284             this.Dispose(false);
285         }
286     }
287 }