summaryrefslogtreecommitdiff
path: root/platform/android
diff options
context:
space:
mode:
authorTor Andersson <tor.andersson@artifex.com>2016-07-15 14:38:42 +0200
committerfred ross-perry <fred.ross-perry@artifex.com>2016-07-15 09:36:56 -0700
commit07bf093b3a11cc776a6a227e8cb478a87d2cdd94 (patch)
tree09ef85881a1ee403154fbcd5cbb2b0adf9232dee /platform/android
parente21010d7b33afd2268c2c2104be296ce6c1a024c (diff)
downloadmupdf-07bf093b3a11cc776a6a227e8cb478a87d2cdd94.tar.xz
Fix indentation for Java sources.
Diffstat (limited to 'platform/android')
-rw-r--r--platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocPageView.java870
-rw-r--r--platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocView.java1784
-rw-r--r--platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/PageAdapter.java96
-rw-r--r--platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/RenderListener.java4
-rw-r--r--platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/Stepper.java62
-rw-r--r--platform/android/viewer/src/com/artifex/mupdfdemo/ArrayDeque.java1578
-rw-r--r--platform/android/viewer/src/com/artifex/mupdfdemo/AsyncTask.java1088
-rw-r--r--platform/android/viewer/src/com/artifex/mupdfdemo/Deque.java846
-rw-r--r--platform/android/viewer/src/com/artifex/mupdfdemo/MuPDFActivity.java2
-rw-r--r--platform/android/viewer/src/com/artifex/mupdfdemo/OutlineActivityData.java2
-rw-r--r--platform/android/viewer/src/com/artifex/mupdfdemo/OutlineAdapter.java4
-rw-r--r--platform/android/viewer/src/com/artifex/mupdfdemo/OutlineItem.java6
-rw-r--r--platform/android/viewer/src/com/artifex/mupdfdemo/PageView.java20
-rw-r--r--platform/android/viewer/src/com/artifex/mupdfdemo/ReaderView.java84
-rw-r--r--platform/android/viewer/src/com/artifex/mupdfdemo/SearchTask.java2
-rw-r--r--platform/android/viewer/src/com/artifex/mupdfdemo/SearchTaskResult.java2
16 files changed, 3225 insertions, 3225 deletions
diff --git a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocPageView.java b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocPageView.java
index 5ab52646..95eb4cdb 100644
--- a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocPageView.java
+++ b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocPageView.java
@@ -23,440 +23,440 @@ import com.artifex.mupdf.fitz.Page;
public class DocPageView extends View implements Callback
{
- private final Document mDoc;
- private int mPageNum = -1;
- private Page mPage;
- private boolean mFinished = false;
-
- private float mScale = 1.0f;
- private float mZoom = 1.0f;
-
- // drawing bitmap
- private Bitmap mDrawBitmap = null;
- private Rect mDrawSrcRect = new Rect();
- private final Rect mDrawDstRect = new Rect();
- private final Rect sourceRect = new Rect();
- private final Rect renderRect = new Rect();
- private float renderScale;
-
- // current size of this view
- private Point mSize;
-
- // for drawing
- private final Paint mPainter;
- private final Rect mSrcRect = new Rect();
- private final Rect mDstRect = new Rect();
- private float drawScale;
-
- private static final boolean DEBUG_PAGE_RENDERING = false;
-
- private static final float mResolution = 160f;
-
- DisplayList pageContents = null;
- DisplayList annotContents = null;
-
- public DocPageView(Context context, Document theDoc)
- {
- super(context);
- setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
-
- mDoc = theDoc;
- mPainter = new Paint();
-
- setFocusable(true);
- setFocusableInTouchMode(true);
- }
-
- public void setupPage(final int thePageNum, int w, int h)
- {
- // if the page number has not yet been set, or has changed,
- // make a new page object.
- if (thePageNum != mPageNum)
- {
- mPageNum = thePageNum;
-
- // de-cache contents and annotations
- if (pageContents != null) {
- pageContents.destroy();
- pageContents = null;
- }
- if (annotContents != null) {
- annotContents.destroy();
- annotContents = null;
- }
-
- // destroy the page before making a new one.
- if (mPage!=null)
- mPage.destroy();
- mPage = mDoc.loadPage(mPageNum);
- }
-
- // calculate zoom that makes page fit
-
- com.artifex.mupdf.fitz.Rect pageBounds = mPage.getBounds();
-
- float pagew = (pageBounds.x1 - pageBounds.x0)*mResolution/72f;
- float pageH = (pageBounds.y1 - pageBounds.y0)*mResolution/72f;
-
- mZoom = w/pagew;
- mSize = new Point((int)(pagew*mZoom), (int)(pageH*mZoom));
- }
-
- public void setNewScale(float scale) {
- mScale = scale;
- }
-
- public int getCalculatedWidth()
- {
- return (int)(mSize.x * mScale);
- }
-
- public int getCalculatedHeight()
- {
- return (int)(mSize.y * mScale);
- }
-
- // a test for real visibility
- private static final Rect visRect = new Rect();
- public boolean isReallyVisible() {
- return getLocalVisibleRect(visRect);
- }
-
- // This function renders colored rectangles and text in place of the page.
- // Use it to test layouts.
- private void renderNoPage(Bitmap bitmap, final RenderListener listener, Rect localVisRect, Rect globalVisRect)
- {
- // specify where to draw to and from
- mDrawBitmap = bitmap;
- mDrawSrcRect.set(globalVisRect);
- mDrawDstRect.set(localVisRect);
-
- // make a rect representing the entire page in screen coordinates
- int[] locations = new int[2];
- getLocationOnScreen(locations);
- Rect pageRect = new Rect(locations[0], locations[1], locations[0]+getWidth(), locations[1]+getHeight());
-
- // draw a yellow page with a red border containing the page number
-
- Paint p = new Paint();
- Canvas c = new Canvas(bitmap);
- p.setColor(Color.RED);
- p.setStyle(Paint.Style.FILL);
- c.drawRect(pageRect,p);
-
- Rect smaller = new Rect(pageRect);
- int inset = (int)(40*mScale);
- smaller.inset(inset, inset);
- p.setColor(Color.YELLOW);
- p.setStyle(Paint.Style.FILL);
- c.drawRect(smaller,p);
-
- String s = "" + (mPageNum+1);
- p.setColor(Color.BLACK);
- p.setTextSize(200.0f*mScale);
- c.drawText(s, pageRect.left+(90*mScale), pageRect.top+(290*mScale), p);
-
- invalidate();
- listener.progress(0);
- }
-
- public void render(Bitmap bitmap, final RenderListener listener)
- {
- if (mFinished)
- return;
-
- // get local visible rect
- Rect localVisRect = new Rect();
- if (!getLocalVisibleRect(localVisRect)) {
- listener.progress(0);
- return; // not visible
- }
-
- // get global visible rect
- Rect globalVisRect = new Rect();
- if (!getGlobalVisibleRect(globalVisRect)) {
- listener.progress(0);
- return; // not visible
- }
-
- // do the render.
- if (DEBUG_PAGE_RENDERING)
- renderNoPage(bitmap, listener, localVisRect, globalVisRect);
- else
- renderPage(bitmap, listener, localVisRect, globalVisRect);
- }
-
- // This function renders the document's page.
- private void renderPage(final Bitmap bitmap, final RenderListener listener, final Rect localVisRect, final Rect globalVisRect)
- {
- // make a rect representing the entire page
- // This might be outside the bounds of the bitmap
- int[] locations = new int[2];
- getLocationOnScreen(locations);
- Rect pageRect = new Rect(locations[0], locations[1], locations[0]+getCalculatedWidth(), locations[1]+getCalculatedHeight());
-
- // make a rect representing the patch
- // clip this to the bitmap
- Rect patchRect = new Rect(pageRect);
- patchRect.left = Math.max(patchRect.left,0);
- patchRect.top = Math.max(patchRect.top,0);
- patchRect.right = Math.min(patchRect.right,bitmap.getWidth());
- patchRect.bottom = Math.min(patchRect.bottom,bitmap.getHeight());
-
- // set up the page and patch coordinates for the device
- int pageX0 = pageRect.left;
- int pageY0 = pageRect.top;
- int pageX1 = pageRect.right;
- int pageY1 = pageRect.bottom;
-
- int patchX0 = patchRect.left;
- int patchY0 = patchRect.top;
- int patchX1 = patchRect.right;
- int patchY1 = patchRect.bottom;
-
- // set up a matrix for scaling
- Matrix ctm = Matrix.Identity();
- ctm.scale(mScale*mZoom*mResolution/72f);
-
- // cache this page's display and annotation lists
- cachePage();
-
- sourceRect.set(globalVisRect);
- renderRect.set(localVisRect);
- renderScale = mScale;
-
- // Render the page in the background
- DrawTaskParams params = new DrawTaskParams(new RenderListener() {
- @Override
- public void progress(int error)
- {
- // specify where to draw to and from
- mDrawBitmap = bitmap;
- mDrawSrcRect.set(sourceRect);
- mDrawDstRect.set(renderRect);
- drawScale = renderScale;
-
- invalidate();
- listener.progress(0);
- }
- }, ctm, bitmap, pageX0, pageY0, pageX1, pageY1, patchX0, patchY0, patchX1, patchY1);
-
- new DrawTask().execute(params, null, null);
- }
-
- private void cachePage()
- {
- Cookie cookie = new Cookie();
-
- if (pageContents==null)
- {
- // run the display list
- pageContents = new DisplayList();
- DisplayListDevice dispDev = new DisplayListDevice(pageContents);
- try {
- mPage.run(dispDev, new Matrix(1, 0, 0, 1, 0, 0), cookie);
- }
- catch (RuntimeException e) {
- pageContents.destroy();
- dispDev.destroy();
- throw(e);
- }
- finally {
- dispDev.destroy();
- }
- }
-
- if (annotContents==null)
- {
- // run the annotation list
- annotContents = new DisplayList();
- DisplayListDevice annotDev = new DisplayListDevice(annotContents);
- try {
- mPage.run(annotDev, new Matrix(1, 0, 0, 1, 0, 0), cookie);
- }
- catch (RuntimeException e) {
- annotContents.destroy();
- annotDev.destroy();
- throw(e);
- }
- finally {
- annotDev.destroy();
- }
- }
- }
-
- @Override
- public void onDraw(Canvas canvas) {
-
- if (mFinished)
- return;
-
- // get bitmap to draw
- Bitmap bitmap = mDrawBitmap;
- if (bitmap==null)
- return; // not yet rendered
-
- // set rectangles for drawing
- mSrcRect.set(mDrawSrcRect);
- mDstRect.set(mDrawDstRect);
-
- // if the scale has changed, adjust the destination
- if (drawScale != mScale)
- {
- mDstRect.left *= (mScale/drawScale);
- mDstRect.top *= (mScale/drawScale);
- mDstRect.right *= (mScale/drawScale);
- mDstRect.bottom *= (mScale/drawScale);
- }
-
- // draw
- canvas.drawBitmap(bitmap, mSrcRect, mDstRect, mPainter);
- }
-
- public boolean onSingleTap(int x, int y) {
- // NOTE: when double-tapping, a single-tap will also happen first.
- // so that must be safe to do.
- return false;
- }
-
- public void onDoubleTap(int x, int y) {
- }
-
- private Point screenToPage(Point p)
- {
- return screenToPage(p.x, p.y);
- }
-
- private Point screenToPage(int screenX, int screenY)
- {
- // convert to view-relative
- int viewX = screenX;
- int viewY = screenY;
- int loc[] = new int[2];
- getLocationOnScreen(loc);
- viewX -= loc[0];
- viewY -= loc[1];
-
- // convert to page-relative
- double factor = mZoom * mScale;
- int pageX = (int)(((double)viewX)/factor);
- int pageY = (int)(((double)viewY)/factor);
-
- return new Point(pageX,pageY);
- }
-
- public Point pageToView(int pageX, int pageY)
- {
- double factor = mZoom * mScale;
-
- int viewX = (int)(((double)pageX)*factor);
- int viewY = (int)(((double)pageY)*factor);
-
- return new Point(viewX, viewY);
- }
-
- public Point viewToPage(int viewX, int viewY)
- {
- double factor = mZoom * mScale;
-
- int pageX = (int)(((double)viewX)/factor);
- int pageY = (int)(((double)viewY)/factor);
-
- return new Point(pageX, pageY);
- }
-
- public void finish()
- {
- mFinished = true;
-
- // destroy the page
- if (mPage!=null) {
- mPage.destroy();
- mPage = null;
- }
- }
-
- // during layout, a DocView-relative rect is calculated and stashed here.
- private final Rect mChildRect = new Rect();
- public void setChildRect(Rect r) {mChildRect.set(r);}
- public Rect getChildRect() {return mChildRect;}
-
- private class DrawTaskParams {
- DrawTaskParams (RenderListener listener, Matrix ctm, Bitmap bitmap,
- int pageX0, int pageY0, int pageX1, int pageY1,
- int patchX0, int patchY0, int patchX1, int patchY1)
- {
- this.listener = listener;
- this.ctm = ctm;
- this.bitmap = bitmap;
- this.pageX0 = pageX0;
- this.pageY0 = pageY0;
- this.pageX1 = pageX1;
- this.pageY1 = pageY1;
- this.patchX0 = patchX0;
- this.patchY0 = patchY0;
- this.patchX1 = patchX1;
- this.patchY1 = patchY1;
- }
-
- public RenderListener listener;
- public Matrix ctm;
- public Bitmap bitmap;
- public int pageX0;
- public int pageY0;
- public int pageX1;
- public int pageY1;
- public int patchX0;
- public int patchY0;
- public int patchX1;
- public int patchY1;
- }
-
- // The definition of our task class
- private class DrawTask extends AsyncTask<DrawTaskParams, Void, Void>
- {
- private DrawTaskParams params = null;
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- }
-
- @Override
- protected Void doInBackground(DrawTaskParams... paramList) {
- params = paramList[0];
-
- Cookie cookie = new Cookie();
- AndroidDrawDevice dev = new AndroidDrawDevice(params.bitmap, params.pageX0, params.pageY0, params.pageX1, params.pageY1, params.patchX0, params.patchY0, params.patchX1, params.patchY1);
- try {
- if (pageContents != null) {
- pageContents.run(dev, params.ctm, cookie);
- }
- if (annotContents != null) {
- annotContents.run(dev, params.ctm, cookie);
- }
- }
- catch (Exception e)
- {
- Log.e("mupdf", e.getMessage());
- }
- finally {
- dev.destroy();
- }
-
- return null;
- }
-
- @Override
- protected void onProgressUpdate(Void... values) {
- super.onProgressUpdate(values);
- }
-
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
- params.listener.progress(0);
- }
- }
+ private final Document mDoc;
+ private int mPageNum = -1;
+ private Page mPage;
+ private boolean mFinished = false;
+
+ private float mScale = 1.0f;
+ private float mZoom = 1.0f;
+
+ // drawing bitmap
+ private Bitmap mDrawBitmap = null;
+ private Rect mDrawSrcRect = new Rect();
+ private final Rect mDrawDstRect = new Rect();
+ private final Rect sourceRect = new Rect();
+ private final Rect renderRect = new Rect();
+ private float renderScale;
+
+ // current size of this view
+ private Point mSize;
+
+ // for drawing
+ private final Paint mPainter;
+ private final Rect mSrcRect = new Rect();
+ private final Rect mDstRect = new Rect();
+ private float drawScale;
+
+ private static final boolean DEBUG_PAGE_RENDERING = false;
+
+ private static final float mResolution = 160f;
+
+ DisplayList pageContents = null;
+ DisplayList annotContents = null;
+
+ public DocPageView(Context context, Document theDoc)
+ {
+ super(context);
+ setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+
+ mDoc = theDoc;
+ mPainter = new Paint();
+
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ }
+
+ public void setupPage(final int thePageNum, int w, int h)
+ {
+ // if the page number has not yet been set, or has changed,
+ // make a new page object.
+ if (thePageNum != mPageNum)
+ {
+ mPageNum = thePageNum;
+
+ // de-cache contents and annotations
+ if (pageContents != null) {
+ pageContents.destroy();
+ pageContents = null;
+ }
+ if (annotContents != null) {
+ annotContents.destroy();
+ annotContents = null;
+ }
+
+ // destroy the page before making a new one.
+ if (mPage!=null)
+ mPage.destroy();
+ mPage = mDoc.loadPage(mPageNum);
+ }
+
+ // calculate zoom that makes page fit
+
+ com.artifex.mupdf.fitz.Rect pageBounds = mPage.getBounds();
+
+ float pagew = (pageBounds.x1 - pageBounds.x0)*mResolution/72f;
+ float pageH = (pageBounds.y1 - pageBounds.y0)*mResolution/72f;
+
+ mZoom = w/pagew;
+ mSize = new Point((int)(pagew*mZoom), (int)(pageH*mZoom));
+ }
+
+ public void setNewScale(float scale) {
+ mScale = scale;
+ }
+
+ public int getCalculatedWidth()
+ {
+ return (int)(mSize.x * mScale);
+ }
+
+ public int getCalculatedHeight()
+ {
+ return (int)(mSize.y * mScale);
+ }
+
+ // a test for real visibility
+ private static final Rect visRect = new Rect();
+ public boolean isReallyVisible() {
+ return getLocalVisibleRect(visRect);
+ }
+
+ // This function renders colored rectangles and text in place of the page.
+ // Use it to test layouts.
+ private void renderNoPage(Bitmap bitmap, final RenderListener listener, Rect localVisRect, Rect globalVisRect)
+ {
+ // specify where to draw to and from
+ mDrawBitmap = bitmap;
+ mDrawSrcRect.set(globalVisRect);
+ mDrawDstRect.set(localVisRect);
+
+ // make a rect representing the entire page in screen coordinates
+ int[] locations = new int[2];
+ getLocationOnScreen(locations);
+ Rect pageRect = new Rect(locations[0], locations[1], locations[0]+getWidth(), locations[1]+getHeight());
+
+ // draw a yellow page with a red border containing the page number
+
+ Paint p = new Paint();
+ Canvas c = new Canvas(bitmap);
+ p.setColor(Color.RED);
+ p.setStyle(Paint.Style.FILL);
+ c.drawRect(pageRect,p);
+
+ Rect smaller = new Rect(pageRect);
+ int inset = (int)(40*mScale);
+ smaller.inset(inset, inset);
+ p.setColor(Color.YELLOW);
+ p.setStyle(Paint.Style.FILL);
+ c.drawRect(smaller,p);
+
+ String s = "" + (mPageNum+1);
+ p.setColor(Color.BLACK);
+ p.setTextSize(200.0f*mScale);
+ c.drawText(s, pageRect.left+(90*mScale), pageRect.top+(290*mScale), p);
+
+ invalidate();
+ listener.progress(0);
+ }
+
+ public void render(Bitmap bitmap, final RenderListener listener)
+ {
+ if (mFinished)
+ return;
+
+ // get local visible rect
+ Rect localVisRect = new Rect();
+ if (!getLocalVisibleRect(localVisRect)) {
+ listener.progress(0);
+ return; // not visible
+ }
+
+ // get global visible rect
+ Rect globalVisRect = new Rect();
+ if (!getGlobalVisibleRect(globalVisRect)) {
+ listener.progress(0);
+ return; // not visible
+ }
+
+ // do the render.
+ if (DEBUG_PAGE_RENDERING)
+ renderNoPage(bitmap, listener, localVisRect, globalVisRect);
+ else
+ renderPage(bitmap, listener, localVisRect, globalVisRect);
+ }
+
+ // This function renders the document's page.
+ private void renderPage(final Bitmap bitmap, final RenderListener listener, final Rect localVisRect, final Rect globalVisRect)
+ {
+ // make a rect representing the entire page
+ // This might be outside the bounds of the bitmap
+ int[] locations = new int[2];
+ getLocationOnScreen(locations);
+ Rect pageRect = new Rect(locations[0], locations[1], locations[0]+getCalculatedWidth(), locations[1]+getCalculatedHeight());
+
+ // make a rect representing the patch
+ // clip this to the bitmap
+ Rect patchRect = new Rect(pageRect);
+ patchRect.left = Math.max(patchRect.left,0);
+ patchRect.top = Math.max(patchRect.top,0);
+ patchRect.right = Math.min(patchRect.right,bitmap.getWidth());
+ patchRect.bottom = Math.min(patchRect.bottom,bitmap.getHeight());
+
+ // set up the page and patch coordinates for the device
+ int pageX0 = pageRect.left;
+ int pageY0 = pageRect.top;
+ int pageX1 = pageRect.right;
+ int pageY1 = pageRect.bottom;
+
+ int patchX0 = patchRect.left;
+ int patchY0 = patchRect.top;
+ int patchX1 = patchRect.right;
+ int patchY1 = patchRect.bottom;
+
+ // set up a matrix for scaling
+ Matrix ctm = Matrix.Identity();
+ ctm.scale(mScale*mZoom*mResolution/72f);
+
+ // cache this page's display and annotation lists
+ cachePage();
+
+ sourceRect.set(globalVisRect);
+ renderRect.set(localVisRect);
+ renderScale = mScale;
+
+ // Render the page in the background
+ DrawTaskParams params = new DrawTaskParams(new RenderListener() {
+ @Override
+ public void progress(int error)
+ {
+ // specify where to draw to and from
+ mDrawBitmap = bitmap;
+ mDrawSrcRect.set(sourceRect);
+ mDrawDstRect.set(renderRect);
+ drawScale = renderScale;
+
+ invalidate();
+ listener.progress(0);
+ }
+ }, ctm, bitmap, pageX0, pageY0, pageX1, pageY1, patchX0, patchY0, patchX1, patchY1);
+
+ new DrawTask().execute(params, null, null);
+ }
+
+ private void cachePage()
+ {
+ Cookie cookie = new Cookie();
+
+ if (pageContents==null)
+ {
+ // run the display list
+ pageContents = new DisplayList();
+ DisplayListDevice dispDev = new DisplayListDevice(pageContents);
+ try {
+ mPage.run(dispDev, new Matrix(1, 0, 0, 1, 0, 0), cookie);
+ }
+ catch (RuntimeException e) {
+ pageContents.destroy();
+ dispDev.destroy();
+ throw(e);
+ }
+ finally {
+ dispDev.destroy();
+ }
+ }
+
+ if (annotContents==null)
+ {
+ // run the annotation list
+ annotContents = new DisplayList();
+ DisplayListDevice annotDev = new DisplayListDevice(annotContents);
+ try {
+ mPage.run(annotDev, new Matrix(1, 0, 0, 1, 0, 0), cookie);
+ }
+ catch (RuntimeException e) {
+ annotContents.destroy();
+ annotDev.destroy();
+ throw(e);
+ }
+ finally {
+ annotDev.destroy();
+ }
+ }
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+
+ if (mFinished)
+ return;
+
+ // get bitmap to draw
+ Bitmap bitmap = mDrawBitmap;
+ if (bitmap==null)
+ return; // not yet rendered
+
+ // set rectangles for drawing
+ mSrcRect.set(mDrawSrcRect);
+ mDstRect.set(mDrawDstRect);
+
+ // if the scale has changed, adjust the destination
+ if (drawScale != mScale)
+ {
+ mDstRect.left *= (mScale/drawScale);
+ mDstRect.top *= (mScale/drawScale);
+ mDstRect.right *= (mScale/drawScale);
+ mDstRect.bottom *= (mScale/drawScale);
+ }
+
+ // draw
+ canvas.drawBitmap(bitmap, mSrcRect, mDstRect, mPainter);
+ }
+
+ public boolean onSingleTap(int x, int y) {
+ // NOTE: when double-tapping, a single-tap will also happen first.
+ // so that must be safe to do.
+ return false;
+ }
+
+ public void onDoubleTap(int x, int y) {
+ }
+
+ private Point screenToPage(Point p)
+ {
+ return screenToPage(p.x, p.y);
+ }
+
+ private Point screenToPage(int screenX, int screenY)
+ {
+ // convert to view-relative
+ int viewX = screenX;
+ int viewY = screenY;
+ int loc[] = new int[2];
+ getLocationOnScreen(loc);
+ viewX -= loc[0];
+ viewY -= loc[1];
+
+ // convert to page-relative
+ double factor = mZoom * mScale;
+ int pageX = (int)(((double)viewX)/factor);
+ int pageY = (int)(((double)viewY)/factor);
+
+ return new Point(pageX,pageY);
+ }
+
+ public Point pageToView(int pageX, int pageY)
+ {
+ double factor = mZoom * mScale;
+
+ int viewX = (int)(((double)pageX)*factor);
+ int viewY = (int)(((double)pageY)*factor);
+
+ return new Point(viewX, viewY);
+ }
+
+ public Point viewToPage(int viewX, int viewY)
+ {
+ double factor = mZoom * mScale;
+
+ int pageX = (int)(((double)viewX)/factor);
+ int pageY = (int)(((double)viewY)/factor);
+
+ return new Point(pageX, pageY);
+ }
+
+ public void finish()
+ {
+ mFinished = true;
+
+ // destroy the page
+ if (mPage!=null) {
+ mPage.destroy();
+ mPage = null;
+ }
+ }
+
+ // during layout, a DocView-relative rect is calculated and stashed here.
+ private final Rect mChildRect = new Rect();
+ public void setChildRect(Rect r) {mChildRect.set(r);}
+ public Rect getChildRect() {return mChildRect;}
+
+ private class DrawTaskParams {
+ DrawTaskParams (RenderListener listener, Matrix ctm, Bitmap bitmap,
+ int pageX0, int pageY0, int pageX1, int pageY1,
+ int patchX0, int patchY0, int patchX1, int patchY1)
+ {
+ this.listener = listener;
+ this.ctm = ctm;
+ this.bitmap = bitmap;
+ this.pageX0 = pageX0;
+ this.pageY0 = pageY0;
+ this.pageX1 = pageX1;
+ this.pageY1 = pageY1;
+ this.patchX0 = patchX0;
+ this.patchY0 = patchY0;
+ this.patchX1 = patchX1;
+ this.patchY1 = patchY1;
+ }
+
+ public RenderListener listener;
+ public Matrix ctm;
+ public Bitmap bitmap;
+ public int pageX0;
+ public int pageY0;
+ public int pageX1;
+ public int pageY1;
+ public int patchX0;
+ public int patchY0;
+ public int patchX1;
+ public int patchY1;
+ }
+
+ // The definition of our task class
+ private class DrawTask extends AsyncTask<DrawTaskParams, Void, Void>
+ {
+ private DrawTaskParams params = null;
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ }
+
+ @Override
+ protected Void doInBackground(DrawTaskParams... paramList) {
+ params = paramList[0];
+
+ Cookie cookie = new Cookie();
+ AndroidDrawDevice dev = new AndroidDrawDevice(params.bitmap, params.pageX0, params.pageY0, params.pageX1, params.pageY1, params.patchX0, params.patchY0, params.patchX1, params.patchY1);
+ try {
+ if (pageContents != null) {
+ pageContents.run(dev, params.ctm, cookie);
+ }
+ if (annotContents != null) {
+ annotContents.run(dev, params.ctm, cookie);
+ }
+ }
+ catch (Exception e)
+ {
+ Log.e("mupdf", e.getMessage());
+ }
+ finally {
+ dev.destroy();
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onProgressUpdate(Void... values) {
+ super.onProgressUpdate(values);
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ params.listener.progress(0);
+ }
+ }
}
diff --git a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocView.java b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocView.java
index 34613dc3..da6d7677 100644
--- a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocView.java
+++ b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/DocView.java
@@ -22,897 +22,897 @@ import android.widget.Scroller;
import com.artifex.mupdf.fitz.Document;
public class DocView
- extends AdapterView<Adapter>
- implements GestureDetector.OnGestureListener, ScaleGestureDetector.OnScaleGestureListener, Runnable
+ extends AdapterView<Adapter>
+ implements GestureDetector.OnGestureListener, ScaleGestureDetector.OnScaleGestureListener, Runnable
{
- private static final int GAP = 20;
-
- private static final float MIN_SCALE = .15f;
- private static final float MAX_SCALE = 5.0f;
-
- private PageAdapter mAdapter;
- private boolean mFinished = false;
-
- private final SparseArray<View> mChildViews = new SparseArray<View>(3);
-
- private boolean mScaling; // Whether the user is currently pinch zooming
- private float mScale = 1.0f;
- private int mXScroll; // Scroll amounts recorded from events.
- private int mYScroll; // and then accounted for in onLayout
-
- private GestureDetector mGestureDetector;
- private ScaleGestureDetector mScaleGestureDetector;
-
- // bitmaps for rendering
- // these are created by the activity and set using setBitmaps()
- private final static double OVERSIZE_FACTOR = 1.1;
- private final Bitmap[] bitmaps = {null,null};
-
- private int bitmapIndex = 0;
- private boolean renderRequested = false;
- private int renderCount = 0;
-
- // used during layout
- private final Rect mChildRect = new Rect();
- private final Rect mViewport = new Rect();
- private final Point mViewportOrigin = new Point();
- private final Rect mBlockRect = new Rect();
- private final Rect mLastBlockRect = new Rect();
- private int mLastLayoutColumns = 1;
- protected int mPageCollectionHeight;
- private int mPageCollectionWidth;
-
- // for flinging
- private static final int MOVING_DIAGONALLY = 0;
- private static final int MOVING_LEFT = 1;
- private static final int MOVING_RIGHT = 2;
- private static final int MOVING_UP = 3;
- private static final int MOVING_DOWN = 4;
-
- private static final float MIN_FLING_VELOCITY = 1500.0f;
- private static final long FLING_THROTTLE_TIME = 20;
-
- private Scroller mScroller;
- private Stepper mStepper;
- private int mScrollerLastX;
- private int mScrollerLastY;
- private long mFlingStartTime;
-
- // for single- and double-tapping
- private long mLastTapTime = 0;
- private float lastTapX;
- private float lastTapY;
- private int mTapStatus = 0;
-
- private static final int DOUBLE_TAP_TIME = 300;
- private static final int SHOW_KEYBOARD_TIME = 500;
-
- // the document.
- private Document mDoc;
-
- private boolean mStarted = false;
-
- public DocView(Context context) {
- super(context);
- initialize(context);
- }
-
- public DocView(Context context, AttributeSet attrs) {
- super(context, attrs);
- initialize(context);
- }
-
- public DocView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- initialize(context);
- }
-
- protected Context mContext = null;
-
- protected void initialize(Context context)
- {
- mContext = context;
- mGestureDetector = new GestureDetector(context, this);
- mScaleGestureDetector = new ScaleGestureDetector(context, this);
- mScroller = new Scroller(context);
- mStepper = new Stepper(this, this);
-
- // create bitmaps
- makeBitmaps();
- }
-
- private void makeBitmaps()
- {
- // get current screen size
- WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- DisplayMetrics metrics = new DisplayMetrics();
- wm.getDefaultDisplay().getMetrics(metrics);
- int screenW = metrics.widthPixels;
- int screenH = metrics.heightPixels;
-
- // make two bitmaps.
- // make them large enough for both screen orientations, so we don't have to
- // change them when the orientation changes.
-
- int w = (int)(screenW*OVERSIZE_FACTOR);
- int h = (int)(screenH*OVERSIZE_FACTOR);
- int size = Math.max(w,h);
- for (int i=0;i<bitmaps.length;i++)
- bitmaps[i] = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
- }
-
- public void start(final String path)
- {
- // wait for the layout to finish
- ViewTreeObserver observer = getViewTreeObserver();
- observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
- {
- @Override
- public void onGlobalLayout()
- {
- getViewTreeObserver().removeOnGlobalLayoutListener(this);
-
- mAdapter = new PageAdapter(mContext);
- mAdapter.setWidth(getWidth());
- mDoc = new Document(path);
- mAdapter.setDocument(mDoc);
- mScale = 1.0f;
- mStarted = true;
- triggerRender();
- }
- });
- }
-
- private void onScaleChild(View v, Float scale)
- {
- ((DocPageView)v).setNewScale(scale);
- }
-
- public void onOrientationChange()
- {
- triggerRender();
- }
-
- private void onSizeChange(float factor)
- {
- mScale *= factor;
- scaleChildren();
- requestLayout();
- }
-
- public boolean onDown(MotionEvent arg0) {
- mScroller.forceFinished(true);
- return true;
- }
-
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
-
- // not while we're scaling
- if (mScaling)
- return true;
-
- // not while a previous fling is underway
- if (!mScroller.isFinished())
- return true;
-
- // must be really flinging
- float vel = Math.max(Math.abs(velocityX),Math.abs(velocityY));
- if (vel<MIN_FLING_VELOCITY)
- return false;
-
- // what direction?
- int direction = directionOfTravel(velocityX,velocityY);
-
- mFlingStartTime = System.currentTimeMillis();
-
- switch (direction)
- {
- case MOVING_DOWN:
- smoothScrollBy(0, getHeight()/2);
- break;
-
- case MOVING_UP:
- smoothScrollBy(0, -getHeight()/2);
- break;
-
- default:
- break;
- }
-
- return true;
- }
-
- private static int directionOfTravel(float vx, float vy) {
- if (Math.abs(vx) > 2 * Math.abs(vy))
- return (vx > 0) ? MOVING_RIGHT : MOVING_LEFT;
- else if (Math.abs(vy) > 2 * Math.abs(vx))
- return (vy > 0) ? MOVING_DOWN : MOVING_UP;
- else
- return MOVING_DIAGONALLY;
- }
-
- public void onLongPress(MotionEvent e) {
- }
-
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
-
- // not if we're scaling
- if (mScaling)
- return true;
-
- // not if a previous fling is underway
- if (!mScroller.isFinished())
- return true;
-
- // accumulate scrolling amount.
- mXScroll -= distanceX;
- mYScroll -= distanceY;
-
- requestLayout();
-
- return true;
- }
-
- public void onShowPress(MotionEvent e) {
- }
-
- protected DocPageView findPageViewContainingPoint(int x, int y, boolean includeMargin)
- {
- for (int i = 0; i < getChildCount(); i++)
- {
- // get the rect for the page
- View child = getChildAt(i);
- Rect childRect = new Rect();
- child.getGlobalVisibleRect(childRect);
-
- // add in the margin
- if (includeMargin)
- {
- childRect.left -= GAP/2;
- childRect.right += GAP/2;
- childRect.top -= GAP/2;
- childRect.bottom += GAP/2;
- }
-
- // see if the rect contains the point
- if (childRect.contains(x,y))
- return (DocPageView)child;
- }
-
- return null;
- }
-
- protected Point eventToScreen(float fx, float fy)
- {
- int x = Math.round(fx);
- int y = Math.round(fy);
- Rect docRect = new Rect();
- getGlobalVisibleRect(docRect);
- x += docRect.left;
- y += docRect.top;
-
- return new Point(x,y);
- }
-
- protected void doSingleTap(float fx, float fy)
- {
- // find the page view that was tapped.
- Point p = eventToScreen(fx,fy);
- final DocPageView dpv = findPageViewContainingPoint(p.x, p.y, false);
- if (dpv==null)
- return;
-
- // see if the age wants to handle the single tap
- boolean handled = dpv.onSingleTap(p.x,p.y);
-
- // if not, ...
- if (!handled)
- {
- // schedule a task in the near future to check if we're still a single-tap.
- final Handler handler = new Handler();
- final Point tappedPoint = p;
- handler.postDelayed(new Runnable()
- {
- @Override
- public void run()
- {
- if (mTapStatus==1)
- {
- // still single
- }
- else
- {
- // double
- }
- mTapStatus = 0;
- }
- }, SHOW_KEYBOARD_TIME);
- }
- }
-
- protected void doDoubleTap(float fx, float fy)
- {
- Point p = eventToScreen(fx,fy);
- DocPageView v = findPageViewContainingPoint(p.x, p.y, false);
- if (v != null) {
- v.onDoubleTap(p.x,p.y);
- }
- }
-
- public boolean onSingleTapUp(final MotionEvent e)
- {
- long now = System.currentTimeMillis();
- if (mLastTapTime!=0 && ((now-mLastTapTime)<DOUBLE_TAP_TIME))
- {
- mTapStatus = 2;
- doDoubleTap(lastTapX,lastTapY);
- mLastTapTime = 0;
- }
- else
- {
- mLastTapTime = now;
- lastTapX = e.getX();
- lastTapY = e.getY();
- doSingleTap(lastTapX, lastTapY);
- mTapStatus = 1;
- }
-
- return false;
- }
-
- private void scaleChildren()
- {
- // scale children
- for (int i=0; i<getPageCount(); i++)
- {
- DocPageView cv = (DocPageView)getOrCreateChild(i);
- cv.setNewScale(mScale);
- }
- }
-
- public boolean onScale(ScaleGestureDetector detector)
- {
- // new scale factor
- float previousScale = mScale;
- mScale = Math.min(Math.max(mScale * detector.getScaleFactor(), MIN_SCALE), MAX_SCALE);
-
- // did we really scale?
- if (mScale == previousScale)
- return true;
-
- // scale children
- scaleChildren();
-
- // maintain focus while scaling
- float currentFocusX = detector.getFocusX();
- float currentFocusY = detector.getFocusY();
- int viewFocusX = (int)currentFocusX + getScrollX();
- int viewFocusY = (int)currentFocusY + getScrollY();
- mXScroll += viewFocusX - viewFocusX * detector.getScaleFactor();
- mYScroll += viewFocusY - viewFocusY * detector.getScaleFactor();
-
- requestLayout();
-
- return true;
- }
-
- public boolean onScaleBegin(ScaleGestureDetector detector) {
-
- mScaling = true;
-
- // Ignore any scroll amounts yet to be accounted for: the
- // screen is not showing the effect of them, so they can
- // only confuse the user
- mXScroll = mYScroll = 0;
-
- return true;
- }
-
- public void onScaleEnd(ScaleGestureDetector detector)
- {
- // When a pinch-scale is done, we want to get n-across
- // to fit properly.
-
- // get current viewport
- Rect viewport = new Rect();
- getGlobalVisibleRect(viewport);
-
- // if we're at one column and wider than the viewport,
- // leave it alone.
- if (mLastLayoutColumns==0 && mPageCollectionWidth>=viewport.width())
- {
- mScaling = false;
- return;
- }
-
- // ratio of the viewport width to layout width
- float ratio = ((float)(viewport.width()))/((float)(mPageCollectionWidth));
-
- // set a new scale factor
- mScale *= ratio;
- scaleChildren();
-
- // scroll so the left edged is flush to the viewport.
- mXScroll +=getScrollX();
-
- requestLayout();
-
- mScaling = false;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
-
- if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
- // do something when user interaction begins
- }
-
- if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
- // do something when user interaction ends
- triggerRender();
- }
-
- mScaleGestureDetector.onTouchEvent(event);
- mGestureDetector.onTouchEvent(event);
-
- return true;
- }
-
- protected int getPageCount()
- {
- return getAdapter().getCount();
- }
-
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-
- super.onLayout(changed, left, top, right, bottom);
-
- if (!mStarted)
- return;
-
- // not if there are no pages
- if (getPageCount()==0)
- return;
-
- int numDocPages = getPageCount();
-
- // not if we've been finished
- if (finished())
- return;
-
- // perform any pending scrolling
- scrollBy(-mXScroll, -mYScroll);
- mXScroll = mYScroll = 0;
-
- // get current viewport
- mViewportOrigin.set(getScrollX(), getScrollY());
- getGlobalVisibleRect(mViewport);
- mViewport.offsetTo(mViewportOrigin.x, mViewportOrigin.y);
-
- // find the widest child
- int maxw = 0;
- for (int i=0; i<getPageCount(); i++)
- {
- DocPageView cv = (DocPageView)getOrCreateChild(i);
-
- int childWidth = cv.getCalculatedWidth();
- if (childWidth>maxw)
- maxw = childWidth;
- }
-
- // how many columns?
- double dcol = (double)(mViewport.width()+GAP)/(double)(maxw+GAP);
- int columns = (int) dcol;
-
- // lay them out
- int mostVisibleChildHeight = -1;
- int childTop = 0;
- mPageCollectionHeight = 0;
- mPageCollectionWidth = 0;
- int column = 0;
- mBlockRect.setEmpty();
-
- for (int i=0; i<getPageCount(); i++)
- {
- DocPageView cv = (DocPageView)getOrCreateChild(i);
- int childWidth = cv.getCalculatedWidth();
- int childHeight = cv.getCalculatedHeight();
- int childLeft = column * (maxw + GAP);
- int childRight = childLeft + childWidth;
- int childBottom = childTop + childHeight;
- mChildRect.set(childLeft, childTop, childRight, childBottom);
-
- // stash the rect in the page view for later use.
- cv.setChildRect(mChildRect);
-
- // at each layout, we remember the entire width and height of the laid-out
- // pages. This is used in applying constraints to scrolling amounts.
- if (childBottom> mPageCollectionHeight)
- mPageCollectionHeight = childBottom;
- if (childRight>mPageCollectionWidth)
- mPageCollectionWidth = childRight;
-
- if (mBlockRect.isEmpty())
- mBlockRect.set(mChildRect);
- else
- mBlockRect.union(mChildRect);
-
- if (mChildRect.intersect(mViewport) && i<numDocPages)
- {
- // visible, so include in layout
- if (cv.getParent()==null)
- addChildToLayout(cv);
- cv.layout(childLeft, childTop, childRight, childBottom);
- cv.invalidate();
- }
- else
- {
- // not visible, so remove from layout
- removeViewInLayout(cv);
- }
-
- column++;
- if (column >= columns) {
- column = 0;
- childTop += childHeight;
- childTop += GAP;
- }
- }
-
- // if the number of columns has changed, do some scrolling to adjust
- if (mScaling && columns>=1 && mLastLayoutColumns>=1 && mLastLayoutColumns!=columns)
- {
- // x - center in the viewport
- int dx = mBlockRect.centerX() - mViewport.centerX();
- scrollBy(dx,0);
-
- // y - attempt to keep what's in the center of the viewport in view.
- int oldy = mViewport.centerY() - mLastBlockRect.top;
- int newy = (int)((float)oldy*mBlockRect.height()/mLastBlockRect.height());
- scrollBy(0,newy-oldy);
- }
- mLastLayoutColumns = columns;
- mLastBlockRect.set(mBlockRect);
-
- // see if we're handling a start page
- handleStartPage();
-
- triggerRender();
- }
-
- // start page, get and set.
- private int mStartPage = 0;
- public void setStartPage(int page) {
- mStartPage = page;
- }
- protected int getStartPage() {return mStartPage;}
-
- // handle start page
- public void handleStartPage()
- {
- // if we've been given a start page, go there.
- final int start = getStartPage();
- if (start>0)
- {
- setStartPage(0); // but just once
-
- // post all of this so that we get an additional layout request
- final Handler handler = new Handler();
- handler.post(new Runnable() {
- @Override
- public void run() {
- DocPageView cv = (DocPageView)getOrCreateChild(start-1);
- Rect r = cv.getChildRect();
- scrollBy(0,r.top);
- requestLayout();
- }
- });
- }
- }
-
- // override the view's scrollBy() function so we can
- // take the opportunity to apply some constraints
-
- @Override
- public void scrollBy(int dx, int dy)
- {
- Point p = constrainScrollBy(dx, dy);
- super.scrollBy(p.x, p.y);
- }
-
- // apply contraints to every scroll request.
-
- protected Point constrainScrollBy(int dx, int dy)
- {
- int vph;
- int vpw;
- {
- Rect viewport = new Rect();
- getGlobalVisibleRect(viewport);
- vph = viewport.height();
- vpw = viewport.width();
- }
- int sx = getScrollX();
- int sy = getScrollY();
-
- if (mPageCollectionWidth <= vpw)
- {
- // not too far to the right
- if (mPageCollectionWidth-sx-dx > vpw)
- dx = 0;
-
- // not too far to the left
- if (sx+dx>0)
- dx = -sx;
- }
- else
- {
- // not too far to the right
- if (mPageCollectionWidth < sx+vpw+dx)
- dx = 0;
-
- // not too far to the left
- if (sx+dx < 0)
- dx = -sx;
- }
-
- if (mPageCollectionHeight <= vph)
- {
- // not too far down
- if (mPageCollectionHeight-sy-dy > vph)
- dy = 0;
-
- // not too far up
- if (sy+dy>0)
- dy = -sy;
- }
- else
- {
- // not too far down
- if (sy+dy < 0)
- dy = -sy;
-
- // not too far up.
- if (mPageCollectionHeight+2*vph/3 < sy+vph+dy)
- dy = 0;
- }
-
- return new Point(dx, dy);
- }
-
- @Override
- public Adapter getAdapter() {
- return mAdapter;
- }
-
- @Override
- public View getSelectedView() {
- return null;
- }
-
- @Override
- public void setAdapter(Adapter adapter) {
- mAdapter = (PageAdapter)adapter;
- requestLayout();
- }
-
- @Override
- public void setSelection(int arg0) {
- throw new UnsupportedOperationException("setSelection is not supported");
- }
-
- private View getCached() {
- return null;
- }
-
- protected View getOrCreateChild(int i) {
- View v = mChildViews.get(i);
- if (v == null) {
- v = getViewFromAdapter(i);
- mChildViews.append(i, v); // Record the view against it's adapter index
- onScaleChild(v, mScale);
- }
-
- return v;
- }
-
- protected View getViewFromAdapter(int index)
- {
- return getAdapter().getView(index, getCached(), this);
- }
-
- private void addChildToLayout(View v) {
- LayoutParams params = v.getLayoutParams();
- if (params == null) {
- params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
- }
- addViewInLayout(v, 0, params, true);
- }
-
- private void triggerRender()
- {
- // Note that a render is needed
- renderRequested = true;
-
- // If the previous render has completed, start a new one. Otherwise
- // a new one will start as soon as the previous one completes.
- if (renderCount == 0)
- renderPages();
- }
-
- private void renderPages()
- {
- renderRequested = false;
-
- if (mFinished)
- return;
-
- if (bitmaps == null)
- return;
-
- // Rotate to the next bitmap
- bitmapIndex++;
- if (bitmapIndex>=bitmaps.length)
- bitmapIndex = 0;
-
- // iterate through the children
- for (int i = 0; i < getPageCount(); i++) {
-
- if (mFinished)
- return;
-
- final DocPageView cv = (DocPageView) getOrCreateChild(i);
- if (cv.getParent()!=null && cv.isReallyVisible()) {
- // Count up as we kick off rendering of each visible page
- renderCount++;
- cv.render(bitmaps[bitmapIndex], new RenderListener() {
- @Override
- public void progress(int error) {
-
- if (error==0)
- cv.invalidate();
-
- // Count down as they complete
- renderCount--;
-
- if (renderCount==0) {
- if (renderRequested) {
- // If this phase of rendering has completed and another has
- // been requested, start it now
- renderPages();
- }
- }
- }
- });
- }
- }
- }
-
- @Override
- public void run()
- {
- if (!mScroller.isFinished())
- {
- mScroller.computeScrollOffset();
- int x = mScroller.getCurrX();
- int y = mScroller.getCurrY();
- mXScroll += x - mScrollerLastX;
- mYScroll += y - mScrollerLastY;
- mScrollerLastX = x;
- mScrollerLastY = y;
-
- // limit the amount of repeated layouts.
- long tNow = System.currentTimeMillis();
- long diff = tNow - mFlingStartTime;
- if (diff>FLING_THROTTLE_TIME)
- {
- requestLayout();
- mFlingStartTime = tNow;
- }
-
- mStepper.prod();
- }
- else
- {
- // one more
- long tNow = System.currentTimeMillis();
- if (tNow != mFlingStartTime)
- requestLayout();
- }
- }
-
- public void finish()
- {
- // we're done with this view.
- mFinished = true;
-
- // first, hide and remove all the children
- for (int i=0; i<getPageCount(); i++)
- {
- DocPageView cv = (DocPageView)getOrCreateChild(i);
- cv.setVisibility(GONE);
- removeViewInLayout(cv);
- cv.finish();
- }
- }
-
- public boolean finished() {return mFinished;}
-
- protected void smoothScrollBy(int dx, int dy)
- {
- mScrollerLastX = mScrollerLastY = 0;
- mScroller.startScroll(0, 0, dx, dy, 400);
- mStepper.prod();
- }
-
- public void scrollToPage(int pageNumber)
- {
- // scroll to bring the page into view
-
- // get current viewport
- Rect viewport = new Rect();
- getGlobalVisibleRect(viewport);
-
- // offset it based on current scroll position
- Point viewportOrigin = new Point();
- viewportOrigin.set(getScrollX(), getScrollY());
- viewport.offsetTo(viewportOrigin.x, viewportOrigin.y);
-
- // get page rect from last layout
- DocPageView cv = (DocPageView)getOrCreateChild(pageNumber);
- Rect childRect = cv.getChildRect();
-
- // scroll
- if ((childRect.height()) > viewport.height())
- {
- // put the top of the page at the top and the left at 0
- smoothScrollBy(getScrollX(),getScrollY()-childRect.top);
- }
- else
- {
- // if the whole page is not visible, move the center of the page at the center
- if (childRect.top < viewport.top || childRect.bottom > viewport.bottom)
- {
- if (childRect.top==0)
- smoothScrollBy(0, getScrollY());
- else
- smoothScrollBy(0, getScrollY() + viewport.height() / 2 - (childRect.bottom + childRect.top) / 2);
- }
- }
- }
-
- private Point viewToScreen(Point p)
- {
- Point newPoint = new Point(p);
-
- Rect r = new Rect();
- this.getGlobalVisibleRect(r);
-
- newPoint.offset(r.left, r.top);
-
- return newPoint;
- }
-
- public void scrollBoxIntoView (int pageNum, RectF box)
- {
- // get our viewport
- Rect viewport = new Rect();
- getGlobalVisibleRect(viewport);
- viewport.offset(0,-viewport.top);
-
- // get the location of the box's lower left corner,
- // relative to the viewport
- DocPageView cv = (DocPageView)getOrCreateChild(pageNum);
- Point point = cv.pageToView((int)box.left,(int)box.bottom);
- Rect childRect = cv.getChildRect();
- point.y += childRect.top;
- point.y -= getScrollY();
-
- // if the point is outside the viewport, scroll so it is.
- if (point.y<viewport.top || point.y>viewport.bottom)
- {
- int diff = (viewport.top + viewport.bottom)/2 - point.y;
- smoothScrollBy(0,diff);
- }
- }
+ private static final int GAP = 20;
+
+ private static final float MIN_SCALE = .15f;
+ private static final float MAX_SCALE = 5.0f;
+
+ private PageAdapter mAdapter;
+ private boolean mFinished = false;
+
+ private final SparseArray<View> mChildViews = new SparseArray<View>(3);
+
+ private boolean mScaling; // Whether the user is currently pinch zooming
+ private float mScale = 1.0f;
+ private int mXScroll; // Scroll amounts recorded from events.
+ private int mYScroll; // and then accounted for in onLayout
+
+ private GestureDetector mGestureDetector;
+ private ScaleGestureDetector mScaleGestureDetector;
+
+ // bitmaps for rendering
+ // these are created by the activity and set using setBitmaps()
+ private final static double OVERSIZE_FACTOR = 1.1;
+ private final Bitmap[] bitmaps = {null,null};
+
+ private int bitmapIndex = 0;
+ private boolean renderRequested = false;
+ private int renderCount = 0;
+
+ // used during layout
+ private final Rect mChildRect = new Rect();
+ private final Rect mViewport = new Rect();
+ private final Point mViewportOrigin = new Point();
+ private final Rect mBlockRect = new Rect();
+ private final Rect mLastBlockRect = new Rect();
+ private int mLastLayoutColumns = 1;
+ protected int mPageCollectionHeight;
+ private int mPageCollectionWidth;
+
+ // for flinging
+ private static final int MOVING_DIAGONALLY = 0;
+ private static final int MOVING_LEFT = 1;
+ private static final int MOVING_RIGHT = 2;
+ private static final int MOVING_UP = 3;
+ private static final int MOVING_DOWN = 4;
+
+ private static final float MIN_FLING_VELOCITY = 1500.0f;
+ private static final long FLING_THROTTLE_TIME = 20;
+
+ private Scroller mScroller;
+ private Stepper mStepper;
+ private int mScrollerLastX;
+ private int mScrollerLastY;
+ private long mFlingStartTime;
+
+ // for single- and double-tapping
+ private long mLastTapTime = 0;
+ private float lastTapX;
+ private float lastTapY;
+ private int mTapStatus = 0;
+
+ private static final int DOUBLE_TAP_TIME = 300;
+ private static final int SHOW_KEYBOARD_TIME = 500;
+
+ // the document.
+ private Document mDoc;
+
+ private boolean mStarted = false;
+
+ public DocView(Context context) {
+ super(context);
+ initialize(context);
+ }
+
+ public DocView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initialize(context);
+ }
+
+ public DocView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initialize(context);
+ }
+
+ protected Context mContext = null;
+
+ protected void initialize(Context context)
+ {
+ mContext = context;
+ mGestureDetector = new GestureDetector(context, this);
+ mScaleGestureDetector = new ScaleGestureDetector(context, this);
+ mScroller = new Scroller(context);
+ mStepper = new Stepper(this, this);
+
+ // create bitmaps
+ makeBitmaps();
+ }
+
+ private void makeBitmaps()
+ {
+ // get current screen size
+ WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ DisplayMetrics metrics = new DisplayMetrics();
+ wm.getDefaultDisplay().getMetrics(metrics);
+ int screenW = metrics.widthPixels;
+ int screenH = metrics.heightPixels;
+
+ // make two bitmaps.
+ // make them large enough for both screen orientations, so we don't have to
+ // change them when the orientation changes.
+
+ int w = (int)(screenW*OVERSIZE_FACTOR);
+ int h = (int)(screenH*OVERSIZE_FACTOR);
+ int size = Math.max(w,h);
+ for (int i=0;i<bitmaps.length;i++)
+ bitmaps[i] = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ }
+
+ public void start(final String path)
+ {
+ // wait for the layout to finish
+ ViewTreeObserver observer = getViewTreeObserver();
+ observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
+ {
+ @Override
+ public void onGlobalLayout()
+ {
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+
+ mAdapter = new PageAdapter(mContext);
+ mAdapter.setWidth(getWidth());
+ mDoc = new Document(path);
+ mAdapter.setDocument(mDoc);
+ mScale = 1.0f;
+ mStarted = true;
+ triggerRender();
+ }
+ });
+ }
+
+ private void onScaleChild(View v, Float scale)
+ {
+ ((DocPageView)v).setNewScale(scale);
+ }
+
+ public void onOrientationChange()
+ {
+ triggerRender();
+ }
+
+ private void onSizeChange(float factor)
+ {
+ mScale *= factor;
+ scaleChildren();
+ requestLayout();
+ }
+
+ public boolean onDown(MotionEvent arg0) {
+ mScroller.forceFinished(true);
+ return true;
+ }
+
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+
+ // not while we're scaling
+ if (mScaling)
+ return true;
+
+ // not while a previous fling is underway
+ if (!mScroller.isFinished())
+ return true;
+
+ // must be really flinging
+ float vel = Math.max(Math.abs(velocityX),Math.abs(velocityY));
+ if (vel<MIN_FLING_VELOCITY)
+ return false;
+
+ // what direction?
+ int direction = directionOfTravel(velocityX,velocityY);
+
+ mFlingStartTime = System.currentTimeMillis();
+
+ switch (direction)
+ {
+ case MOVING_DOWN:
+ smoothScrollBy(0, getHeight()/2);
+ break;
+
+ case MOVING_UP:
+ smoothScrollBy(0, -getHeight()/2);
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+ }
+
+ private static int directionOfTravel(float vx, float vy) {
+ if (Math.abs(vx) > 2 * Math.abs(vy))
+ return (vx > 0) ? MOVING_RIGHT : MOVING_LEFT;
+ else if (Math.abs(vy) > 2 * Math.abs(vx))
+ return (vy > 0) ? MOVING_DOWN : MOVING_UP;
+ else
+ return MOVING_DIAGONALLY;
+ }
+
+ public void onLongPress(MotionEvent e) {
+ }
+
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+
+ // not if we're scaling
+ if (mScaling)
+ return true;
+
+ // not if a previous fling is underway
+ if (!mScroller.isFinished())
+ return true;
+
+ // accumulate scrolling amount.
+ mXScroll -= distanceX;
+ mYScroll -= distanceY;
+
+ requestLayout();
+
+ return true;
+ }
+
+ public void onShowPress(MotionEvent e) {
+ }
+
+ protected DocPageView findPageViewContainingPoint(int x, int y, boolean includeMargin)
+ {
+ for (int i = 0; i < getChildCount(); i++)
+ {
+ // get the rect for the page
+ View child = getChildAt(i);
+ Rect childRect = new Rect();
+ child.getGlobalVisibleRect(childRect);
+
+ // add in the margin
+ if (includeMargin)
+ {
+ childRect.left -= GAP/2;
+ childRect.right += GAP/2;
+ childRect.top -= GAP/2;
+ childRect.bottom += GAP/2;
+ }
+
+ // see if the rect contains the point
+ if (childRect.contains(x,y))
+ return (DocPageView)child;
+ }
+
+ return null;
+ }
+
+ protected Point eventToScreen(float fx, float fy)
+ {
+ int x = Math.round(fx);
+ int y = Math.round(fy);
+ Rect docRect = new Rect();
+ getGlobalVisibleRect(docRect);
+ x += docRect.left;
+ y += docRect.top;
+
+ return new Point(x,y);
+ }
+
+ protected void doSingleTap(float fx, float fy)
+ {
+ // find the page view that was tapped.
+ Point p = eventToScreen(fx,fy);
+ final DocPageView dpv = findPageViewContainingPoint(p.x, p.y, false);
+ if (dpv==null)
+ return;
+
+ // see if the age wants to handle the single tap
+ boolean handled = dpv.onSingleTap(p.x,p.y);
+
+ // if not, ...
+ if (!handled)
+ {
+ // schedule a task in the near future to check if we're still a single-tap.
+ final Handler handler = new Handler();
+ final Point tappedPoint = p;
+ handler.postDelayed(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ if (mTapStatus==1)
+ {
+ // still single
+ }
+ else
+ {
+ // double
+ }
+ mTapStatus = 0;
+ }
+ }, SHOW_KEYBOARD_TIME);
+ }
+ }
+
+ protected void doDoubleTap(float fx, float fy)
+ {
+ Point p = eventToScreen(fx,fy);
+ DocPageView v = findPageViewContainingPoint(p.x, p.y, false);
+ if (v != null) {
+ v.onDoubleTap(p.x,p.y);
+ }
+ }
+
+ public boolean onSingleTapUp(final MotionEvent e)
+ {
+ long now = System.currentTimeMillis();
+ if (mLastTapTime!=0 && ((now-mLastTapTime)<DOUBLE_TAP_TIME))
+ {
+ mTapStatus = 2;
+ doDoubleTap(lastTapX,lastTapY);
+ mLastTapTime = 0;
+ }
+ else
+ {
+ mLastTapTime = now;
+ lastTapX = e.getX();
+ lastTapY = e.getY();
+ doSingleTap(lastTapX, lastTapY);
+ mTapStatus = 1;
+ }
+
+ return false;
+ }
+
+ private void scaleChildren()
+ {
+ // scale children
+ for (int i=0; i<getPageCount(); i++)
+ {
+ DocPageView cv = (DocPageView)getOrCreateChild(i);
+ cv.setNewScale(mScale);
+ }
+ }
+
+ public boolean onScale(ScaleGestureDetector detector)
+ {
+ // new scale factor
+ float previousScale = mScale;
+ mScale = Math.min(Math.max(mScale * detector.getScaleFactor(), MIN_SCALE), MAX_SCALE);
+
+ // did we really scale?
+ if (mScale == previousScale)
+ return true;
+
+ // scale children
+ scaleChildren();
+
+ // maintain focus while scaling
+ float currentFocusX = detector.getFocusX();
+ float currentFocusY = detector.getFocusY();
+ int viewFocusX = (int)currentFocusX + getScrollX();
+ int viewFocusY = (int)currentFocusY + getScrollY();
+ mXScroll += viewFocusX - viewFocusX * detector.getScaleFactor();
+ mYScroll += viewFocusY - viewFocusY * detector.getScaleFactor();
+
+ requestLayout();
+
+ return true;
+ }
+
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+
+ mScaling = true;
+
+ // Ignore any scroll amounts yet to be accounted for: the
+ // screen is not showing the effect of them, so they can
+ // only confuse the user
+ mXScroll = mYScroll = 0;
+
+ return true;
+ }
+
+ public void onScaleEnd(ScaleGestureDetector detector)
+ {
+ // When a pinch-scale is done, we want to get n-across
+ // to fit properly.
+
+ // get current viewport
+ Rect viewport = new Rect();
+ getGlobalVisibleRect(viewport);
+
+ // if we're at one column and wider than the viewport,
+ // leave it alone.
+ if (mLastLayoutColumns==0 && mPageCollectionWidth>=viewport.width())
+ {
+ mScaling = false;
+ return;
+ }
+
+ // ratio of the viewport width to layout width
+ float ratio = ((float)(viewport.width()))/((float)(mPageCollectionWidth));
+
+ // set a new scale factor
+ mScale *= ratio;
+ scaleChildren();
+
+ // scroll so the left edged is flush to the viewport.
+ mXScroll +=getScrollX();
+
+ requestLayout();
+
+ mScaling = false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+
+ if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
+ // do something when user interaction begins
+ }
+
+ if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
+ // do something when user interaction ends
+ triggerRender();
+ }
+
+ mScaleGestureDetector.onTouchEvent(event);
+ mGestureDetector.onTouchEvent(event);
+
+ return true;
+ }
+
+ protected int getPageCount()
+ {
+ return getAdapter().getCount();
+ }
+
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+
+ super.onLayout(changed, left, top, right, bottom);
+
+ if (!mStarted)
+ return;
+
+ // not if there are no pages
+ if (getPageCount()==0)
+ return;
+
+ int numDocPages = getPageCount();
+
+ // not if we've been finished
+ if (finished())
+ return;
+
+ // perform any pending scrolling
+ scrollBy(-mXScroll, -mYScroll);
+ mXScroll = mYScroll = 0;
+
+ // get current viewport
+ mViewportOrigin.set(getScrollX(), getScrollY());
+ getGlobalVisibleRect(mViewport);
+ mViewport.offsetTo(mViewportOrigin.x, mViewportOrigin.y);
+
+ // find the widest child
+ int maxw = 0;
+ for (int i=0; i<getPageCount(); i++)
+ {
+ DocPageView cv = (DocPageView)getOrCreateChild(i);
+
+ int childWidth = cv.getCalculatedWidth();
+ if (childWidth>maxw)
+ maxw = childWidth;
+ }
+
+ // how many columns?
+ double dcol = (double)(mViewport.width()+GAP)/(double)(maxw+GAP);
+ int columns = (int) dcol;
+
+ // lay them out
+ int mostVisibleChildHeight = -1;
+ int childTop = 0;
+ mPageCollectionHeight = 0;
+ mPageCollectionWidth = 0;
+ int column = 0;
+ mBlockRect.setEmpty();
+
+ for (int i=0; i<getPageCount(); i++)
+ {
+ DocPageView cv = (DocPageView)getOrCreateChild(i);
+ int childWidth = cv.getCalculatedWidth();
+ int childHeight = cv.getCalculatedHeight();
+ int childLeft = column * (maxw + GAP);
+ int childRight = childLeft + childWidth;
+ int childBottom = childTop + childHeight;
+ mChildRect.set(childLeft, childTop, childRight, childBottom);
+
+ // stash the rect in the page view for later use.
+ cv.setChildRect(mChildRect);
+
+ // at each layout, we remember the entire width and height of the laid-out
+ // pages. This is used in applying constraints to scrolling amounts.
+ if (childBottom> mPageCollectionHeight)
+ mPageCollectionHeight = childBottom;
+ if (childRight>mPageCollectionWidth)
+ mPageCollectionWidth = childRight;
+
+ if (mBlockRect.isEmpty())
+ mBlockRect.set(mChildRect);
+ else
+ mBlockRect.union(mChildRect);
+
+ if (mChildRect.intersect(mViewport) && i<numDocPages)
+ {
+ // visible, so include in layout
+ if (cv.getParent()==null)
+ addChildToLayout(cv);
+ cv.layout(childLeft, childTop, childRight, childBottom);
+ cv.invalidate();
+ }
+ else
+ {
+ // not visible, so remove from layout
+ removeViewInLayout(cv);
+ }
+
+ column++;
+ if (column >= columns) {
+ column = 0;
+ childTop += childHeight;
+ childTop += GAP;
+ }
+ }
+
+ // if the number of columns has changed, do some scrolling to adjust
+ if (mScaling && columns>=1 && mLastLayoutColumns>=1 && mLastLayoutColumns!=columns)
+ {
+ // x - center in the viewport
+ int dx = mBlockRect.centerX() - mViewport.centerX();
+ scrollBy(dx,0);
+
+ // y - attempt to keep what's in the center of the viewport in view.
+ int oldy = mViewport.centerY() - mLastBlockRect.top;
+ int newy = (int)((float)oldy*mBlockRect.height()/mLastBlockRect.height());
+ scrollBy(0,newy-oldy);
+ }
+ mLastLayoutColumns = columns;
+ mLastBlockRect.set(mBlockRect);
+
+ // see if we're handling a start page
+ handleStartPage();
+
+ triggerRender();
+ }
+
+ // start page, get and set.
+ private int mStartPage = 0;
+ public void setStartPage(int page) {
+ mStartPage = page;
+ }
+ protected int getStartPage() {return mStartPage;}
+
+ // handle start page
+ public void handleStartPage()
+ {
+ // if we've been given a start page, go there.
+ final int start = getStartPage();
+ if (start>0)
+ {
+ setStartPage(0); // but just once
+
+ // post all of this so that we get an additional layout request
+ final Handler handler = new Handler();
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ DocPageView cv = (DocPageView)getOrCreateChild(start-1);
+ Rect r = cv.getChildRect();
+ scrollBy(0,r.top);
+ requestLayout();
+ }
+ });
+ }
+ }
+
+ // override the view's scrollBy() function so we can
+ // take the opportunity to apply some constraints
+
+ @Override
+ public void scrollBy(int dx, int dy)
+ {
+ Point p = constrainScrollBy(dx, dy);
+ super.scrollBy(p.x, p.y);
+ }
+
+ // apply contraints to every scroll request.
+
+ protected Point constrainScrollBy(int dx, int dy)
+ {
+ int vph;
+ int vpw;
+ {
+ Rect viewport = new Rect();
+ getGlobalVisibleRect(viewport);
+ vph = viewport.height();
+ vpw = viewport.width();
+ }
+ int sx = getScrollX();
+ int sy = getScrollY();
+
+ if (mPageCollectionWidth <= vpw)
+ {
+ // not too far to the right
+ if (mPageCollectionWidth-sx-dx > vpw)
+ dx = 0;
+
+ // not too far to the left
+ if (sx+dx>0)
+ dx = -sx;
+ }
+ else
+ {
+ // not too far to the right
+ if (mPageCollectionWidth < sx+vpw+dx)
+ dx = 0;
+
+ // not too far to the left
+ if (sx+dx < 0)
+ dx = -sx;
+ }
+
+ if (mPageCollectionHeight <= vph)
+ {
+ // not too far down
+ if (mPageCollectionHeight-sy-dy > vph)
+ dy = 0;
+
+ // not too far up
+ if (sy+dy>0)
+ dy = -sy;
+ }
+ else
+ {
+ // not too far down
+ if (sy+dy < 0)
+ dy = -sy;
+
+ // not too far up.
+ if (mPageCollectionHeight+2*vph/3 < sy+vph+dy)
+ dy = 0;
+ }
+
+ return new Point(dx, dy);
+ }
+
+ @Override
+ public Adapter getAdapter() {
+ return mAdapter;
+ }
+
+ @Override
+ public View getSelectedView() {
+ return null;
+ }
+
+ @Override
+ public void setAdapter(Adapter adapter) {
+ mAdapter = (PageAdapter)adapter;
+ requestLayout();
+ }
+
+ @Override
+ public void setSelection(int arg0) {
+ throw new UnsupportedOperationException("setSelection is not supported");
+ }
+
+ private View getCached() {
+ return null;
+ }
+
+ protected View getOrCreateChild(int i) {
+ View v = mChildViews.get(i);
+ if (v == null) {
+ v = getViewFromAdapter(i);
+ mChildViews.append(i, v); // Record the view against it's adapter index
+ onScaleChild(v, mScale);
+ }
+
+ return v;
+ }
+
+ protected View getViewFromAdapter(int index)
+ {
+ return getAdapter().getView(index, getCached(), this);
+ }
+
+ private void addChildToLayout(View v) {
+ LayoutParams params = v.getLayoutParams();
+ if (params == null) {
+ params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ }
+ addViewInLayout(v, 0, params, true);
+ }
+
+ private void triggerRender()
+ {
+ // Note that a render is needed
+ renderRequested = true;
+
+ // If the previous render has completed, start a new one. Otherwise
+ // a new one will start as soon as the previous one completes.
+ if (renderCount == 0)
+ renderPages();
+ }
+
+ private void renderPages()
+ {
+ renderRequested = false;
+
+ if (mFinished)
+ return;
+
+ if (bitmaps == null)
+ return;
+
+ // Rotate to the next bitmap
+ bitmapIndex++;
+ if (bitmapIndex>=bitmaps.length)
+ bitmapIndex = 0;
+
+ // iterate through the children
+ for (int i = 0; i < getPageCount(); i++) {
+
+ if (mFinished)
+ return;
+
+ final DocPageView cv = (DocPageView) getOrCreateChild(i);
+ if (cv.getParent()!=null && cv.isReallyVisible()) {
+ // Count up as we kick off rendering of each visible page
+ renderCount++;
+ cv.render(bitmaps[bitmapIndex], new RenderListener() {
+ @Override
+ public void progress(int error) {
+
+ if (error==0)
+ cv.invalidate();
+
+ // Count down as they complete
+ renderCount--;
+
+ if (renderCount==0) {
+ if (renderRequested) {
+ // If this phase of rendering has completed and another has
+ // been requested, start it now
+ renderPages();
+ }
+ }
+ }
+ });
+ }
+ }
+ }
+
+ @Override
+ public void run()
+ {
+ if (!mScroller.isFinished())
+ {
+ mScroller.computeScrollOffset();
+ int x = mScroller.getCurrX();
+ int y = mScroller.getCurrY();
+ mXScroll += x - mScrollerLastX;
+ mYScroll += y - mScrollerLastY;
+ mScrollerLastX = x;
+ mScrollerLastY = y;
+
+ // limit the amount of repeated layouts.
+ long tNow = System.currentTimeMillis();
+ long diff = tNow - mFlingStartTime;
+ if (diff>FLING_THROTTLE_TIME)
+ {
+ requestLayout();
+ mFlingStartTime = tNow;
+ }
+
+ mStepper.prod();
+ }
+ else
+ {
+ // one more
+ long tNow = System.currentTimeMillis();
+ if (tNow != mFlingStartTime)
+ requestLayout();
+ }
+ }
+
+ public void finish()
+ {
+ // we're done with this view.
+ mFinished = true;
+
+ // first, hide and remove all the children
+ for (int i=0; i<getPageCount(); i++)
+ {
+ DocPageView cv = (DocPageView)getOrCreateChild(i);
+ cv.setVisibility(GONE);
+ removeViewInLayout(cv);
+ cv.finish();
+ }
+ }
+
+ public boolean finished() {return mFinished;}
+
+ protected void smoothScrollBy(int dx, int dy)
+ {
+ mScrollerLastX = mScrollerLastY = 0;
+ mScroller.startScroll(0, 0, dx, dy, 400);
+ mStepper.prod();
+ }
+
+ public void scrollToPage(int pageNumber)
+ {
+ // scroll to bring the page into view
+
+ // get current viewport
+ Rect viewport = new Rect();
+ getGlobalVisibleRect(viewport);
+
+ // offset it based on current scroll position
+ Point viewportOrigin = new Point();
+ viewportOrigin.set(getScrollX(), getScrollY());
+ viewport.offsetTo(viewportOrigin.x, viewportOrigin.y);
+
+ // get page rect from last layout
+ DocPageView cv = (DocPageView)getOrCreateChild(pageNumber);
+ Rect childRect = cv.getChildRect();
+
+ // scroll
+ if ((childRect.height()) > viewport.height())
+ {
+ // put the top of the page at the top and the left at 0
+ smoothScrollBy(getScrollX(),getScrollY()-childRect.top);
+ }
+ else
+ {
+ // if the whole page is not visible, move the center of the page at the center
+ if (childRect.top < viewport.top || childRect.bottom > viewport.bottom)
+ {
+ if (childRect.top==0)
+ smoothScrollBy(0, getScrollY());
+ else
+ smoothScrollBy(0, getScrollY() + viewport.height() / 2 - (childRect.bottom + childRect.top) / 2);
+ }
+ }
+ }
+
+ private Point viewToScreen(Point p)
+ {
+ Point newPoint = new Point(p);
+
+ Rect r = new Rect();
+ this.getGlobalVisibleRect(r);
+
+ newPoint.offset(r.left, r.top);
+
+ return newPoint;
+ }
+
+ public void scrollBoxIntoView (int pageNum, RectF box)
+ {
+ // get our viewport
+ Rect viewport = new Rect();
+ getGlobalVisibleRect(viewport);
+ viewport.offset(0,-viewport.top);
+
+ // get the location of the box's lower left corner,
+ // relative to the viewport
+ DocPageView cv = (DocPageView)getOrCreateChild(pageNum);
+ Point point = cv.pageToView((int)box.left,(int)box.bottom);
+ Rect childRect = cv.getChildRect();
+ point.y += childRect.top;
+ point.y -= getScrollY();
+
+ // if the point is outside the viewport, scroll so it is.
+ if (point.y<viewport.top || point.y>viewport.bottom)
+ {
+ int diff = (viewport.top + viewport.bottom)/2 - point.y;
+ smoothScrollBy(0,diff);
+ }
+ }
}
diff --git a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/PageAdapter.java b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/PageAdapter.java
index 46a93068..0a1d8c05 100644
--- a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/PageAdapter.java
+++ b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/PageAdapter.java
@@ -10,52 +10,52 @@ import com.artifex.mupdf.fitz.Document;
public class PageAdapter extends BaseAdapter
{
- private final Context mContext;
- private Document mDoc;
-
- public PageAdapter(Context c) {
- mContext = c;
- }
-
- public void setDocument(Document doc) {
- mDoc = doc;
- }
- private int mWidth;
- public void setWidth(int w) {mWidth=w;}
-
- @Override
- public int getCount() {
- return mDoc.countPages();
- }
-
- public Object getItem(int position) {
- return null; // not used
- }
-
- public long getItemId(int position) {
- return 0; // not used
- }
-
- public View getView(final int position, View convertView, ViewGroup parent)
- {
- // make or reuse a view
- DocPageView pageView;
-
- final Activity activity = (Activity)mContext;
- if (convertView == null)
- {
- // make a new one
- pageView = new DocPageView(activity, mDoc);
- }
- else
- {
- // reuse an existing one
- pageView = (DocPageView) convertView;
- }
-
- // set up the page
- pageView.setupPage(position, mWidth, 1);
-
- return pageView;
- }
+ private final Context mContext;
+ private Document mDoc;
+
+ public PageAdapter(Context c) {
+ mContext = c;
+ }
+
+ public void setDocument(Document doc) {
+ mDoc = doc;
+ }
+ private int mWidth;
+ public void setWidth(int w) {mWidth=w;}
+
+ @Override
+ public int getCount() {
+ return mDoc.countPages();
+ }
+
+ public Object getItem(int position) {
+ return null; // not used
+ }
+
+ public long getItemId(int position) {
+ return 0; // not used
+ }
+
+ public View getView(final int position, View convertView, ViewGroup parent)
+ {
+ // make or reuse a view
+ DocPageView pageView;
+
+ final Activity activity = (Activity)mContext;
+ if (convertView == null)
+ {
+ // make a new one
+ pageView = new DocPageView(activity, mDoc);
+ }
+ else
+ {
+ // reuse an existing one
+ pageView = (DocPageView) convertView;
+ }
+
+ // set up the page
+ pageView.setupPage(position, mWidth, 1);
+
+ return pageView;
+ }
}
diff --git a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/RenderListener.java b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/RenderListener.java
index 7030ef8d..37a3432b 100644
--- a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/RenderListener.java
+++ b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/RenderListener.java
@@ -2,5 +2,5 @@ package com.artifex.mupdf.android;
public interface RenderListener
{
- void progress(int error);
-} \ No newline at end of file
+ void progress(int error);
+}
diff --git a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/Stepper.java b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/Stepper.java
index 8a6b29a5..9275cf99 100644
--- a/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/Stepper.java
+++ b/platform/android/example/mupdf/src/main/java/com/artifex/mupdf/android/Stepper.java
@@ -5,38 +5,38 @@ import android.os.Build;
import android.view.View;
public class Stepper {
- private final View mPoster;
- private final Runnable mTask;
- private boolean mPending;
+ private final View mPoster;
+ private final Runnable mTask;
+ private boolean mPending;
- public Stepper(View v, Runnable r) {
- mPoster = v;
- mTask = r;
- mPending = false;
- }
+ public Stepper(View v, Runnable r) {
+ mPoster = v;
+ mTask = r;
+ mPending = false;
+ }
- @SuppressLint("NewApi")
- public void prod() {
- if (!mPending) {
- mPending = true;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- mPoster.postOnAnimation(new Runnable() {
- @Override
- public void run() {
- mPending = false;
- mTask.run();
- }
- });
- } else {
- mPoster.post(new Runnable() {
- @Override
- public void run() {
- mPending = false;
- mTask.run();
- }
- });
+ @SuppressLint("NewApi")
+ public void prod() {
+ if (!mPending) {
+ mPending = true;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ mPoster.postOnAnimation(new Runnable() {
+ @Override
+ public void run() {
+ mPending = false;
+ mTask.run();
+ }
+ });
+ } else {
+ mPoster.post(new Runnable() {
+ @Override
+ public void run() {
+ mPending = false;
+ mTask.run();
+ }
+ });
- }
- }
- }
+ }
+ }
+ }
}
diff --git a/platform/android/viewer/src/com/artifex/mupdfdemo/ArrayDeque.java b/platform/android/viewer/src/com/artifex/mupdfdemo/ArrayDeque.java
index 4f06ea41..c8549058 100644
--- a/platform/android/viewer/src/com/artifex/mupdfdemo/ArrayDeque.java
+++ b/platform/android/viewer/src/com/artifex/mupdfdemo/ArrayDeque.java
@@ -62,794 +62,794 @@ import java.util.Stack;
* @param <E> the type of elements held in this collection
*/
public class ArrayDeque<E> extends AbstractCollection<E>
- implements Deque<E>, Cloneable, java.io.Serializable
+ implements Deque<E>, Cloneable, java.io.Serializable
{
- /**
- * The array in which the elements of the deque are stored.
- * The capacity of the deque is the length of this array, which is
- * always a power of two. The array is never allowed to become
- * full, except transiently within an addX method where it is
- * resized (see doubleCapacity) immediately upon becoming full,
- * thus avoiding head and tail wrapping around to equal each
- * other. We also guarantee that all array cells not holding
- * deque elements are always null.
- */
- private transient Object[] elements;
-
- /**
- * The index of the element at the head of the deque (which is the
- * element that would be removed by remove() or pop()); or an
- * arbitrary number equal to tail if the deque is empty.
- */
- private transient int head;
-
- /**
- * The index at which the next element would be added to the tail
- * of the deque (via addLast(E), add(E), or push(E)).
- */
- private transient int tail;
-
- /**
- * The minimum capacity that we'll use for a newly created deque.
- * Must be a power of 2.
- */
- private static final int MIN_INITIAL_CAPACITY = 8;
-
- // ****** Array allocation and resizing utilities ******
-
- /**
- * Allocate empty array to hold the given number of elements.
- *
- * @param numElements the number of elements to hold
- */
- private void allocateElements(int numElements) {
- int initialCapacity = MIN_INITIAL_CAPACITY;
- // Find the best power of two to hold elements.
- // Tests "<=" because arrays aren't kept full.
- if (numElements >= initialCapacity) {
- initialCapacity = numElements;
- initialCapacity |= (initialCapacity >>> 1);
- initialCapacity |= (initialCapacity >>> 2);
- initialCapacity |= (initialCapacity >>> 4);
- initialCapacity |= (initialCapacity >>> 8);
- initialCapacity |= (initialCapacity >>> 16);
- initialCapacity++;
-
- if (initialCapacity < 0) // Too many elements, must back off
- initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
- }
- elements = new Object[initialCapacity];
- }
-
- /**
- * Double the capacity of this deque. Call only when full, i.e.,
- * when head and tail have wrapped around to become equal.
- */
- private void doubleCapacity() {
- // assert head == tail;
- int p = head;
- int n = elements.length;
- int r = n - p; // number of elements to the right of p
- int newCapacity = n << 1;
- if (newCapacity < 0)
- throw new IllegalStateException("Sorry, deque too big");
- Object[] a = new Object[newCapacity];
- System.arraycopy(elements, p, a, 0, r);
- System.arraycopy(elements, 0, a, r, p);
- elements = a;
- head = 0;
- tail = n;
- }
-
- /**
- * Copies the elements from our element array into the specified array,
- * in order (from first to last element in the deque). It is assumed
- * that the array is large enough to hold all elements in the deque.
- *
- * @return its argument
- */
- private <T> T[] copyElements(T[] a) {
- if (head < tail) {
- System.arraycopy(elements, head, a, 0, size());
- } else if (head > tail) {
- int headPortionLen = elements.length - head;
- System.arraycopy(elements, head, a, 0, headPortionLen);
- System.arraycopy(elements, 0, a, headPortionLen, tail);
- }
- return a;
- }
-
- /**
- * Constructs an empty array deque with an initial capacity
- * sufficient to hold 16 elements.
- */
- public ArrayDeque() {
- elements = new Object[16];
- }
-
- /**
- * Constructs an empty array deque with an initial capacity
- * sufficient to hold the specified number of elements.
- *
- * @param numElements lower bound on initial capacity of the deque
- */
- public ArrayDeque(int numElements) {
- allocateElements(numElements);
- }
-
- /**
- * Constructs a deque containing the elements of the specified
- * collection, in the order they are returned by the collection's
- * iterator. (The first element returned by the collection's
- * iterator becomes the first element, or <i>front</i> of the
- * deque.)
- *
- * @param c the collection whose elements are to be placed into the deque
- * @throws NullPointerException if the specified collection is null
- */
- public ArrayDeque(Collection<? extends E> c) {
- allocateElements(c.size());
- addAll(c);
- }
-
- // The main insertion and extraction methods are addFirst,
- // addLast, pollFirst, pollLast. The other methods are defined in
- // terms of these.
-
- /**
- * Inserts the specified element at the front of this deque.
- *
- * @param e the element to add
- * @throws NullPointerException if the specified element is null
- */
- public void addFirst(E e) {
- if (e == null)
- throw new NullPointerException("e == null");
- elements[head = (head - 1) & (elements.length - 1)] = e;
- if (head == tail)
- doubleCapacity();
- }
-
- /**
- * Inserts the specified element at the end of this deque.
- *
- * <p>This method is equivalent to {@link #add}.
- *
- * @param e the element to add
- * @throws NullPointerException if the specified element is null
- */
- public void addLast(E e) {
- if (e == null)
- throw new NullPointerException("e == null");
- elements[tail] = e;
- if ( (tail = (tail + 1) & (elements.length - 1)) == head)
- doubleCapacity();
- }
-
- /**
- * Inserts the specified element at the front of this deque.
- *
- * @param e the element to add
- * @return <tt>true</tt> (as specified by {@link Deque#offerFirst})
- * @throws NullPointerException if the specified element is null
- */
- public boolean offerFirst(E e) {
- addFirst(e);
- return true;
- }
-
- /**
- * Inserts the specified element at the end of this deque.
- *
- * @param e the element to add
- * @return <tt>true</tt> (as specified by {@link Deque#offerLast})
- * @throws NullPointerException if the specified element is null
- */
- public boolean offerLast(E e) {
- addLast(e);
- return true;
- }
-
- /**
- * @throws NoSuchElementException {@inheritDoc}
- */
- public E removeFirst() {
- E x = pollFirst();
- if (x == null)
- throw new NoSuchElementException();
- return x;
- }
-
- /**
- * @throws NoSuchElementException {@inheritDoc}
- */
- public E removeLast() {
- E x = pollLast();
- if (x == null)
- throw new NoSuchElementException();
- return x;
- }
-
- public E pollFirst() {
- int h = head;
- @SuppressWarnings("unchecked") E result = (E) elements[h];
- // Element is null if deque empty
- if (result == null)
- return null;
- elements[h] = null; // Must null out slot
- head = (h + 1) & (elements.length - 1);
- return result;
- }
-
- public E pollLast() {
- int t = (tail - 1) & (elements.length - 1);
- @SuppressWarnings("unchecked") E result = (E) elements[t];
- if (result == null)
- return null;
- elements[t] = null;
- tail = t;
- return result;
- }
-
- /**
- * @throws NoSuchElementException {@inheritDoc}
- */
- public E getFirst() {
- @SuppressWarnings("unchecked") E result = (E) elements[head];
- if (result == null)
- throw new NoSuchElementException();
- return result;
- }
-
- /**
- * @throws NoSuchElementException {@inheritDoc}
- */
- public E getLast() {
- @SuppressWarnings("unchecked")
- E result = (E) elements[(tail - 1) & (elements.length - 1)];
- if (result == null)
- throw new NoSuchElementException();
- return result;
- }
-
- public E peekFirst() {
- @SuppressWarnings("unchecked") E result = (E) elements[head];
- // elements[head] is null if deque empty
- return result;
- }
-
- public E peekLast() {
- @SuppressWarnings("unchecked")
- E result = (E) elements[(tail - 1) & (elements.length - 1)];
- return result;
- }
-
- /**
- * Removes the first occurrence of the specified element in this
- * deque (when traversing the deque from head to tail).
- * If the deque does not contain the element, it is unchanged.
- * More formally, removes the first element <tt>e</tt> such that
- * <tt>o.equals(e)</tt> (if such an element exists).
- * Returns <tt>true</tt> if this deque contained the specified element
- * (or equivalently, if this deque changed as a result of the call).
- *
- * @param o element to be removed from this deque, if present
- * @return <tt>true</tt> if the deque contained the specified element
- */
- public boolean removeFirstOccurrence(Object o) {
- if (o == null)
- return false;
- int mask = elements.length - 1;
- int i = head;
- Object x;
- while ( (x = elements[i]) != null) {
- if (o.equals(x)) {
- delete(i);
- return true;
- }
- i = (i + 1) & mask;
- }
- return false;
- }
-
- /**
- * Removes the last occurrence of the specified element in this
- * deque (when traversing the deque from head to tail).
- * If the deque does not contain the element, it is unchanged.
- * More formally, removes the last element <tt>e</tt> such that
- * <tt>o.equals(e)</tt> (if such an element exists).
- * Returns <tt>true</tt> if this deque contained the specified element
- * (or equivalently, if this deque changed as a result of the call).
- *
- * @param o element to be removed from this deque, if present
- * @return <tt>true</tt> if the deque contained the specified element
- */
- public boolean removeLastOccurrence(Object o) {
- if (o == null)
- return false;
- int mask = elements.length - 1;
- int i = (tail - 1) & mask;
- Object x;
- while ( (x = elements[i]) != null) {
- if (o.equals(x)) {
- delete(i);
- return true;
- }
- i = (i - 1) & mask;
- }
- return false;
- }
-
- // *** Queue methods ***
-
- /**
- * Inserts the specified element at the end of this deque.
- *
- * <p>This method is equivalent to {@link #addLast}.
- *
- * @param e the element to add
- * @return <tt>true</tt> (as specified by {@link Collection#add})
- * @throws NullPointerException if the specified element is null
- */
- public boolean add(E e) {
- addLast(e);
- return true;
- }
-
- /**
- * Inserts the specified element at the end of this deque.
- *
- * <p>This method is equivalent to {@link #offerLast}.
- *
- * @param e the element to add
- * @return <tt>true</tt> (as specified by {@link Queue#offer})
- * @throws NullPointerException if the specified element is null
- */
- public boolean offer(E e) {
- return offerLast(e);
- }
-
- /**
- * Retrieves and removes the head of the queue represented by this deque.
- *
- * This method differs from {@link #poll poll} only in that it throws an
- * exception if this deque is empty.
- *
- * <p>This method is equivalent to {@link #removeFirst}.
- *
- * @return the head of the queue represented by this deque
- * @throws NoSuchElementException {@inheritDoc}
- */
- public E remove() {
- return removeFirst();
- }
-
- /**
- * Retrieves and removes the head of the queue represented by this deque
- * (in other words, the first element of this deque), or returns
- * <tt>null</tt> if this deque is empty.
- *
- * <p>This method is equivalent to {@link #pollFirst}.
- *
- * @return the head of the queue represented by this deque, or
- * <tt>null</tt> if this deque is empty
- */
- public E poll() {
- return pollFirst();
- }
-
- /**
- * Retrieves, but does not remove, the head of the queue represented by
- * this deque. This method differs from {@link #peek peek} only in
- * that it throws an exception if this deque is empty.
- *
- * <p>This method is equivalent to {@link #getFirst}.
- *
- * @return the head of the queue represented by this deque
- * @throws NoSuchElementException {@inheritDoc}
- */
- public E element() {
- return getFirst();
- }
-
- /**
- * Retrieves, but does not remove, the head of the queue represented by
- * this deque, or returns <tt>null</tt> if this deque is empty.
- *
- * <p>This method is equivalent to {@link #peekFirst}.
- *
- * @return the head of the queue represented by this deque, or
- * <tt>null</tt> if this deque is empty
- */
- public E peek() {
- return peekFirst();
- }
-
- // *** Stack methods ***
-
- /**
- * Pushes an element onto the stack represented by this deque. In other
- * words, inserts the element at the front of this deque.
- *
- * <p>This method is equivalent to {@link #addFirst}.
- *
- * @param e the element to push
- * @throws NullPointerException if the specified element is null
- */
- public void push(E e) {
- addFirst(e);
- }
-
- /**
- * Pops an element from the stack represented by this deque. In other
- * words, removes and returns the first element of this deque.
- *
- * <p>This method is equivalent to {@link #removeFirst()}.
- *
- * @return the element at the front of this deque (which is the top
- * of the stack represented by this deque)
- * @throws NoSuchElementException {@inheritDoc}
- */
- public E pop() {
- return removeFirst();
- }
-
- private void checkInvariants() {
- // assert elements[tail] == null;
- // assert head == tail ? elements[head] == null :
- // (elements[head] != null &&
- // elements[(tail - 1) & (elements.length - 1)] != null);
- // assert elements[(head - 1) & (elements.length - 1)] == null;
- }
-
- /**
- * Removes the element at the specified position in the elements array,
- * adjusting head and tail as necessary. This can result in motion of
- * elements backwards or forwards in the array.
- *
- * <p>This method is called delete rather than remove to emphasize
- * that its semantics differ from those of {@link List#remove(int)}.
- *
- * @return true if elements moved backwards
- */
- private boolean delete(int i) {
- //checkInvariants();
- final Object[] elements = this.elements;
- final int mask = elements.length - 1;
- final int h = head;
- final int t = tail;
- final int front = (i - h) & mask;
- final int back = (t - i) & mask;
-
- // Invariant: head <= i < tail mod circularity
- if (front >= ((t - h) & mask))
- throw new ConcurrentModificationException();
-
- // Optimize for least element motion
- if (front < back) {
- if (h <= i) {
- System.arraycopy(elements, h, elements, h + 1, front);
- } else { // Wrap around
- System.arraycopy(elements, 0, elements, 1, i);
- elements[0] = elements[mask];
- System.arraycopy(elements, h, elements, h + 1, mask - h);
- }
- elements[h] = null;
- head = (h + 1) & mask;
- return false;
- } else {
- if (i < t) { // Copy the null tail as well
- System.arraycopy(elements, i + 1, elements, i, back);
- tail = t - 1;
- } else { // Wrap around
- System.arraycopy(elements, i + 1, elements, i, mask - i);
- elements[mask] = elements[0];
- System.arraycopy(elements, 1, elements, 0, t);
- tail = (t - 1) & mask;
- }
- return true;
- }
- }
-
- // *** Collection Methods ***
-
- /**
- * Returns the number of elements in this deque.
- *
- * @return the number of elements in this deque
- */
- public int size() {
- return (tail - head) & (elements.length - 1);
- }
-
- /**
- * Returns <tt>true</tt> if this deque contains no elements.
- *
- * @return <tt>true</tt> if this deque contains no elements
- */
- public boolean isEmpty() {
- return head == tail;
- }
-
- /**
- * Returns an iterator over the elements in this deque. The elements
- * will be ordered from first (head) to last (tail). This is the same
- * order that elements would be dequeued (via successive calls to
- * {@link #remove} or popped (via successive calls to {@link #pop}).
- *
- * @return an iterator over the elements in this deque
- */
- public Iterator<E> iterator() {
- return new DeqIterator();
- }
-
- public Iterator<E> descendingIterator() {
- return new DescendingIterator();
- }
-
- private class DeqIterator implements Iterator<E> {
- /**
- * Index of element to be returned by subsequent call to next.
- */
- private int cursor = head;
-
- /**
- * Tail recorded at construction (also in remove), to stop
- * iterator and also to check for comodification.
- */
- private int fence = tail;
-
- /**
- * Index of element returned by most recent call to next.
- * Reset to -1 if element is deleted by a call to remove.
- */
- private int lastRet = -1;
-
- public boolean hasNext() {
- return cursor != fence;
- }
-
- public E next() {
- if (cursor == fence)
- throw new NoSuchElementException();
- @SuppressWarnings("unchecked") E result = (E) elements[cursor];
- // This check doesn't catch all possible comodifications,
- // but does catch the ones that corrupt traversal
- if (tail != fence || result == null)
- throw new ConcurrentModificationException();
- lastRet = cursor;
- cursor = (cursor + 1) & (elements.length - 1);
- return result;
- }
-
- public void remove() {
- if (lastRet < 0)
- throw new IllegalStateException();
- if (delete(lastRet)) { // if left-shifted, undo increment in next()
- cursor = (cursor - 1) & (elements.length - 1);
- fence = tail;
- }
- lastRet = -1;
- }
- }
-
- private class DescendingIterator implements Iterator<E> {
- /*
- * This class is nearly a mirror-image of DeqIterator, using
- * tail instead of head for initial cursor, and head instead of
- * tail for fence.
- */
- private int cursor = tail;
- private int fence = head;
- private int lastRet = -1;
-
- public boolean hasNext() {
- return cursor != fence;
- }
-
- public E next() {
- if (cursor == fence)
- throw new NoSuchElementException();
- cursor = (cursor - 1) & (elements.length - 1);
- @SuppressWarnings("unchecked") E result = (E) elements[cursor];
- if (head != fence || result == null)
- throw new ConcurrentModificationException();
- lastRet = cursor;
- return result;
- }
-
- public void remove() {
- if (lastRet < 0)
- throw new IllegalStateException();
- if (!delete(lastRet)) {
- cursor = (cursor + 1) & (elements.length - 1);
- fence = head;
- }
- lastRet = -1;
- }
- }
-
- /**
- * Returns <tt>true</tt> if this deque contains the specified element.
- * More formally, returns <tt>true</tt> if and only if this deque contains
- * at least one element <tt>e</tt> such that <tt>o.equals(e)</tt>.
- *
- * @param o object to be checked for containment in this deque
- * @return <tt>true</tt> if this deque contains the specified element
- */
- public boolean contains(Object o) {
- if (o == null)
- return false;
- int mask = elements.length - 1;
- int i = head;
- Object x;
- while ( (x = elements[i]) != null) {
- if (o.equals(x))
- return true;
- i = (i + 1) & mask;
- }
- return false;
- }
-
- /**
- * Removes a single instance of the specified element from this deque.
- * If the deque does not contain the element, it is unchanged.
- * More formally, removes the first element <tt>e</tt> such that
- * <tt>o.equals(e)</tt> (if such an element exists).
- * Returns <tt>true</tt> if this deque contained the specified element
- * (or equivalently, if this deque changed as a result of the call).
- *
- * <p>This method is equivalent to {@link #removeFirstOccurrence}.
- *
- * @param o element to be removed from this deque, if present
- * @return <tt>true</tt> if this deque contained the specified element
- */
- public boolean remove(Object o) {
- return removeFirstOccurrence(o);
- }
-
- /**
- * Removes all of the elements from this deque.
- * The deque will be empty after this call returns.
- */
- public void clear() {
- int h = head;
- int t = tail;
- if (h != t) { // clear all cells
- head = tail = 0;
- int i = h;
- int mask = elements.length - 1;
- do {
- elements[i] = null;
- i = (i + 1) & mask;
- } while (i != t);
- }
- }
-
- /**
- * Returns an array containing all of the elements in this deque
- * in proper sequence (from first to last element).
- *
- * <p>The returned array will be "safe" in that no references to it are
- * maintained by this deque. (In other words, this method must allocate
- * a new array). The caller is thus free to modify the returned array.
- *
- * <p>This method acts as bridge between array-based and collection-based
- * APIs.
- *
- * @return an array containing all of the elements in this deque
- */
- public Object[] toArray() {
- return copyElements(new Object[size()]);
- }
-
- /**
- * Returns an array containing all of the elements in this deque in
- * proper sequence (from first to last element); the runtime type of the
- * returned array is that of the specified array. If the deque fits in
- * the specified array, it is returned therein. Otherwise, a new array
- * is allocated with the runtime type of the specified array and the
- * size of this deque.
- *
- * <p>If this deque fits in the specified array with room to spare
- * (i.e., the array has more elements than this deque), the element in
- * the array immediately following the end of the deque is set to
- * <tt>null</tt>.
- *
- * <p>Like the {@link #toArray()} method, this method acts as bridge between
- * array-based and collection-based APIs. Further, this method allows
- * precise control over the runtime type of the output array, and may,
- * under certain circumstances, be used to save allocation costs.
- *
- * <p>Suppose <tt>x</tt> is a deque known to contain only strings.
- * The following code can be used to dump the deque into a newly
- * allocated array of <tt>String</tt>:
- *
- * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
- *
- * Note that <tt>toArray(new Object[0])</tt> is identical in function to
- * <tt>toArray()</tt>.
- *
- * @param a the array into which the elements of the deque are to
- * be stored, if it is big enough; otherwise, a new array of the
- * same runtime type is allocated for this purpose
- * @return an array containing all of the elements in this deque
- * @throws ArrayStoreException if the runtime type of the specified array
- * is not a supertype of the runtime type of every element in
- * this deque
- * @throws NullPointerException if the specified array is null
- */
- @SuppressWarnings("unchecked")
- public <T> T[] toArray(T[] a) {
- int size = size();
- if (a.length < size)
- a = (T[])java.lang.reflect.Array.newInstance(
- a.getClass().getComponentType(), size);
- copyElements(a);
- if (a.length > size)
- a[size] = null;
- return a;
- }
-
- // *** Object methods ***
-
- /**
- * Returns a copy of this deque.
- *
- * @return a copy of this deque
- */
- public ArrayDeque<E> clone() {
- try {
- @SuppressWarnings("unchecked")
- ArrayDeque<E> result = (ArrayDeque<E>) super.clone();
- result.elements = Arrays.copyOf(elements, elements.length);
- return result;
-
- } catch (CloneNotSupportedException e) {
- throw new AssertionError();
- }
- }
-
- /**
- * Appease the serialization gods.
- */
- private static final long serialVersionUID = 2340985798034038923L;
-
- /**
- * Serialize this deque.
- *
- * @serialData The current size (<tt>int</tt>) of the deque,
- * followed by all of its elements (each an object reference) in
- * first-to-last order.
- */
- private void writeObject(java.io.ObjectOutputStream s)
- throws java.io.IOException {
- s.defaultWriteObject();
-
- // Write out size
- s.writeInt(size());
-
- // Write out elements in order.
- int mask = elements.length - 1;
- for (int i = head; i != tail; i = (i + 1) & mask)
- s.writeObject(elements[i]);
- }
-
- /**
- * Deserialize this deque.
- */
- private void readObject(java.io.ObjectInputStream s)
- throws java.io.IOException, ClassNotFoundException {
- s.defaultReadObject();
-
- // Read in size and allocate array
- int size = s.readInt();
- allocateElements(size);
- head = 0;
- tail = size;
-
- // Read in all elements in the proper order.
- for (int i = 0; i < size; i++)
- elements[i] = s.readObject();
- }
+ /**
+ * The array in which the elements of the deque are stored.
+ * The capacity of the deque is the length of this array, which is
+ * always a power of two. The array is never allowed to become
+ * full, except transiently within an addX method where it is
+ * resized (see doubleCapacity) immediately upon becoming full,
+ * thus avoiding head and tail wrapping around to equal each
+ * other. We also guarantee that all array cells not holding
+ * deque elements are always null.
+ */
+ private transient Object[] elements;
+
+ /**
+ * The index of the element at the head of the deque (which is the
+ * element that would be removed by remove() or pop()); or an
+ * arbitrary number equal to tail if the deque is empty.
+ */
+ private transient int head;
+
+ /**
+ * The index at which the next element would be added to the tail
+ * of the deque (via addLast(E), add(E), or push(E)).
+ */
+ private transient int tail;
+
+ /**
+ * The minimum capacity that we'll use for a newly created deque.
+ * Must be a power of 2.
+ */
+ private static final int MIN_INITIAL_CAPACITY = 8;
+
+ // ****** Array allocation and resizing utilities ******
+
+ /**
+ * Allocate empty array to hold the given number of elements.
+ *
+ * @param numElements the number of elements to hold
+ */
+ private void allocateElements(int numElements) {
+ int initialCapacity = MIN_INITIAL_CAPACITY;
+ // Find the best power of two to hold elements.
+ // Tests "<=" because arrays aren't kept full.
+ if (numElements >= initialCapacity) {
+ initialCapacity = numElements;
+ initialCapacity |= (initialCapacity >>> 1);
+ initialCapacity |= (initialCapacity >>> 2);
+ initialCapacity |= (initialCapacity >>> 4);
+ initialCapacity |= (initialCapacity >>> 8);
+ initialCapacity |= (initialCapacity >>> 16);
+ initialCapacity++;
+
+ if (initialCapacity < 0) // Too many elements, must back off
+ initialCapacity >>>= 1; // Good luck allocating 2 ^ 30 elements
+ }
+ elements = new Object[initialCapacity];
+ }
+
+ /**
+ * Double the capacity of this deque. Call only when full, i.e.,
+ * when head and tail have wrapped around to become equal.
+ */
+ private void doubleCapacity() {
+ // assert head == tail;
+ int p = head;
+ int n = elements.length;
+ int r = n - p; // number of elements to the right of p
+ int newCapacity = n << 1;
+ if (newCapacity < 0)
+ throw new IllegalStateException("Sorry, deque too big");
+ Object[] a = new Object[newCapacity];
+ System.arraycopy(elements, p, a, 0, r);
+ System.arraycopy(elements, 0, a, r, p);
+ elements = a;
+ head = 0;
+ tail = n;
+ }
+
+ /**
+ * Copies the elements from our element array into the specified array,
+ * in order (from first to last element in the deque). It is assumed
+ * that the array is large enough to hold all elements in the deque.
+ *
+ * @return its argument
+ */
+ private <T> T[] copyElements(T[] a) {
+ if (head < tail) {
+ System.arraycopy(elements, head, a, 0, size());
+ } else if (head > tail) {
+ int headPortionLen = elements.length - head;
+ System.arraycopy(elements, head, a, 0, headPortionLen);
+ System.arraycopy(elements, 0, a, headPortionLen, tail);
+ }
+ return a;
+ }
+
+ /**
+ * Constructs an empty array deque with an initial capacity
+ * sufficient to hold 16 elements.
+ */
+ public ArrayDeque() {
+ elements = new Object[16];
+ }
+
+ /**
+ * Constructs an empty array deque with an initial capacity
+ * sufficient to hold the specified number of elements.
+ *
+ * @param numElements lower bound on initial capacity of the deque
+ */
+ public ArrayDeque(int numElements) {
+ allocateElements(numElements);
+ }
+
+ /**
+ * Constructs a deque containing the elements of the specified
+ * collection, in the order they are returned by the collection's
+ * iterator. (The first element returned by the collection's
+ * iterator becomes the first element, or <i>front</i> of the
+ * deque.)
+ *
+ * @param c the collection whose elements are to be placed into the deque
+ * @throws NullPointerException if the specified collection is null
+ */
+ public ArrayDeque(Collection<? extends E> c) {
+ allocateElements(c.size());
+ addAll(c);
+ }
+
+ // The main insertion and extraction methods are addFirst,
+ // addLast, pollFirst, pollLast. The other methods are defined in
+ // terms of these.
+
+ /**
+ * Inserts the specified element at the front of this deque.
+ *
+ * @param e the element to add
+ * @throws NullPointerException if the specified element is null
+ */
+ public void addFirst(E e) {
+ if (e == null)
+ throw new NullPointerException("e == null");
+ elements[head = (head - 1) & (elements.length - 1)] = e;
+ if (head == tail)
+ doubleCapacity();
+ }
+
+ /**
+ * Inserts the specified element at the end of this deque.
+ *
+ * <p>This method is equivalent to {@link #add}.
+ *
+ * @param e the element to add
+ * @throws NullPointerException if the specified element is null
+ */
+ public void addLast(E e) {
+ if (e == null)
+ throw new NullPointerException("e == null");
+ elements[tail] = e;
+ if ( (tail = (tail + 1) & (elements.length - 1)) == head)
+ doubleCapacity();
+ }
+
+ /**
+ * Inserts the specified element at the front of this deque.
+ *
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Deque#offerFirst})
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean offerFirst(E e) {
+ addFirst(e);
+ return true;
+ }
+
+ /**
+ * Inserts the specified element at the end of this deque.
+ *
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Deque#offerLast})
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean offerLast(E e) {
+ addLast(e);
+ return true;
+ }
+
+ /**
+ * @throws NoSuchElementException {@inheritDoc}
+ */
+ public E removeFirst() {
+ E x = pollFirst();
+ if (x == null)
+ throw new NoSuchElementException();
+ return x;
+ }
+
+ /**
+ * @throws NoSuchElementException {@inheritDoc}
+ */
+ public E removeLast() {
+ E x = pollLast();
+ if (x == null)
+ throw new NoSuchElementException();
+ return x;
+ }
+
+ public E pollFirst() {
+ int h = head;
+ @SuppressWarnings("unchecked") E result = (E) elements[h];
+ // Element is null if deque empty
+ if (result == null)
+ return null;
+ elements[h] = null; // Must null out slot
+ head = (h + 1) & (elements.length - 1);
+ return result;
+ }
+
+ public E pollLast() {
+ int t = (tail - 1) & (elements.length - 1);
+ @SuppressWarnings("unchecked") E result = (E) elements[t];
+ if (result == null)
+ return null;
+ elements[t] = null;
+ tail = t;
+ return result;
+ }
+
+ /**
+ * @throws NoSuchElementException {@inheritDoc}
+ */
+ public E getFirst() {
+ @SuppressWarnings("unchecked") E result = (E) elements[head];
+ if (result == null)
+ throw new NoSuchElementException();
+ return result;
+ }
+
+ /**
+ * @throws NoSuchElementException {@inheritDoc}
+ */
+ public E getLast() {
+ @SuppressWarnings("unchecked")
+ E result = (E) elements[(tail - 1) & (elements.length - 1)];
+ if (result == null)
+ throw new NoSuchElementException();
+ return result;
+ }
+
+ public E peekFirst() {
+ @SuppressWarnings("unchecked") E result = (E) elements[head];
+ // elements[head] is null if deque empty
+ return result;
+ }
+
+ public E peekLast() {
+ @SuppressWarnings("unchecked")
+ E result = (E) elements[(tail - 1) & (elements.length - 1)];
+ return result;
+ }
+
+ /**
+ * Removes the first occurrence of the specified element in this
+ * deque (when traversing the deque from head to tail).
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the first element <tt>e</tt> such that
+ * <tt>o.equals(e)</tt> (if such an element exists).
+ * Returns <tt>true</tt> if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
+ *
+ * @param o element to be removed from this deque, if present
+ * @return <tt>true</tt> if the deque contained the specified element
+ */
+ public boolean removeFirstOccurrence(Object o) {
+ if (o == null)
+ return false;
+ int mask = elements.length - 1;
+ int i = head;
+ Object x;
+ while ( (x = elements[i]) != null) {
+ if (o.equals(x)) {
+ delete(i);
+ return true;
+ }
+ i = (i + 1) & mask;
+ }
+ return false;
+ }
+
+ /**
+ * Removes the last occurrence of the specified element in this
+ * deque (when traversing the deque from head to tail).
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the last element <tt>e</tt> such that
+ * <tt>o.equals(e)</tt> (if such an element exists).
+ * Returns <tt>true</tt> if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
+ *
+ * @param o element to be removed from this deque, if present
+ * @return <tt>true</tt> if the deque contained the specified element
+ */
+ public boolean removeLastOccurrence(Object o) {
+ if (o == null)
+ return false;
+ int mask = elements.length - 1;
+ int i = (tail - 1) & mask;
+ Object x;
+ while ( (x = elements[i]) != null) {
+ if (o.equals(x)) {
+ delete(i);
+ return true;
+ }
+ i = (i - 1) & mask;
+ }
+ return false;
+ }
+
+ // *** Queue methods ***
+
+ /**
+ * Inserts the specified element at the end of this deque.
+ *
+ * <p>This method is equivalent to {@link #addLast}.
+ *
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Collection#add})
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean add(E e) {
+ addLast(e);
+ return true;
+ }
+
+ /**
+ * Inserts the specified element at the end of this deque.
+ *
+ * <p>This method is equivalent to {@link #offerLast}.
+ *
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Queue#offer})
+ * @throws NullPointerException if the specified element is null
+ */
+ public boolean offer(E e) {
+ return offerLast(e);
+ }
+
+ /**
+ * Retrieves and removes the head of the queue represented by this deque.
+ *
+ * This method differs from {@link #poll poll} only in that it throws an
+ * exception if this deque is empty.
+ *
+ * <p>This method is equivalent to {@link #removeFirst}.
+ *
+ * @return the head of the queue represented by this deque
+ * @throws NoSuchElementException {@inheritDoc}
+ */
+ public E remove() {
+ return removeFirst();
+ }
+
+ /**
+ * Retrieves and removes the head of the queue represented by this deque
+ * (in other words, the first element of this deque), or returns
+ * <tt>null</tt> if this deque is empty.
+ *
+ * <p>This method is equivalent to {@link #pollFirst}.
+ *
+ * @return the head of the queue represented by this deque, or
+ * <tt>null</tt> if this deque is empty
+ */
+ public E poll() {
+ return pollFirst();
+ }
+
+ /**
+ * Retrieves, but does not remove, the head of the queue represented by
+ * this deque. This method differs from {@link #peek peek} only in
+ * that it throws an exception if this deque is empty.
+ *
+ * <p>This method is equivalent to {@link #getFirst}.
+ *
+ * @return the head of the queue represented by this deque
+ * @throws NoSuchElementException {@inheritDoc}
+ */
+ public E element() {
+ return getFirst();
+ }
+
+ /**
+ * Retrieves, but does not remove, the head of the queue represented by
+ * this deque, or returns <tt>null</tt> if this deque is empty.
+ *
+ * <p>This method is equivalent to {@link #peekFirst}.
+ *
+ * @return the head of the queue represented by this deque, or
+ * <tt>null</tt> if this deque is empty
+ */
+ public E peek() {
+ return peekFirst();
+ }
+
+ // *** Stack methods ***
+
+ /**
+ * Pushes an element onto the stack represented by this deque. In other
+ * words, inserts the element at the front of this deque.
+ *
+ * <p>This method is equivalent to {@link #addFirst}.
+ *
+ * @param e the element to push
+ * @throws NullPointerException if the specified element is null
+ */
+ public void push(E e) {
+ addFirst(e);
+ }
+
+ /**
+ * Pops an element from the stack represented by this deque. In other
+ * words, removes and returns the first element of this deque.
+ *
+ * <p>This method is equivalent to {@link #removeFirst()}.
+ *
+ * @return the element at the front of this deque (which is the top
+ * of the stack represented by this deque)
+ * @throws NoSuchElementException {@inheritDoc}
+ */
+ public E pop() {
+ return removeFirst();
+ }
+
+ private void checkInvariants() {
+ // assert elements[tail] == null;
+ // assert head == tail ? elements[head] == null :
+ // (elements[head] != null &&
+ // elements[(tail - 1) & (elements.length - 1)] != null);
+ // assert elements[(head - 1) & (elements.length - 1)] == null;
+ }
+
+ /**
+ * Removes the element at the specified position in the elements array,
+ * adjusting head and tail as necessary. This can result in motion of
+ * elements backwards or forwards in the array.
+ *
+ * <p>This method is called delete rather than remove to emphasize
+ * that its semantics differ from those of {@link List#remove(int)}.
+ *
+ * @return true if elements moved backwards
+ */
+ private boolean delete(int i) {
+ //checkInvariants();
+ final Object[] elements = this.elements;
+ final int mask = elements.length - 1;
+ final int h = head;
+ final int t = tail;
+ final int front = (i - h) & mask;
+ final int back = (t - i) & mask;
+
+ // Invariant: head <= i < tail mod circularity
+ if (front >= ((t - h) & mask))
+ throw new ConcurrentModificationException();
+
+ // Optimize for least element motion
+ if (front < back) {
+ if (h <= i) {
+ System.arraycopy(elements, h, elements, h + 1, front);
+ } else { // Wrap around
+ System.arraycopy(elements, 0, elements, 1, i);
+ elements[0] = elements[mask];
+ System.arraycopy(elements, h, elements, h + 1, mask - h);
+ }
+ elements[h] = null;
+ head = (h + 1) & mask;
+ return false;
+ } else {
+ if (i < t) { // Copy the null tail as well
+ System.arraycopy(elements, i + 1, elements, i, back);
+ tail = t - 1;
+ } else { // Wrap around
+ System.arraycopy(elements, i + 1, elements, i, mask - i);
+ elements[mask] = elements[0];
+ System.arraycopy(elements, 1, elements, 0, t);
+ tail = (t - 1) & mask;
+ }
+ return true;
+ }
+ }
+
+ // *** Collection Methods ***
+
+ /**
+ * Returns the number of elements in this deque.
+ *
+ * @return the number of elements in this deque
+ */
+ public int size() {
+ return (tail - head) & (elements.length - 1);
+ }
+
+ /**
+ * Returns <tt>true</tt> if this deque contains no elements.
+ *
+ * @return <tt>true</tt> if this deque contains no elements
+ */
+ public boolean isEmpty() {
+ return head == tail;
+ }
+
+ /**
+ * Returns an iterator over the elements in this deque. The elements
+ * will be ordered from first (head) to last (tail). This is the same
+ * order that elements would be dequeued (via successive calls to
+ * {@link #remove} or popped (via successive calls to {@link #pop}).
+ *
+ * @return an iterator over the elements in this deque
+ */
+ public Iterator<E> iterator() {
+ return new DeqIterator();
+ }
+
+ public Iterator<E> descendingIterator() {
+ return new DescendingIterator();
+ }
+
+ private class DeqIterator implements Iterator<E> {
+ /**
+ * Index of element to be returned by subsequent call to next.
+ */
+ private int cursor = head;
+
+ /**
+ * Tail recorded at construction (also in remove), to stop
+ * iterator and also to check for comodification.
+ */
+ private int fence = tail;
+
+ /**
+ * Index of element returned by most recent call to next.
+ * Reset to -1 if element is deleted by a call to remove.
+ */
+ private int lastRet = -1;
+
+ public boolean hasNext() {
+ return cursor != fence;
+ }
+
+ public E next() {
+ if (cursor == fence)
+ throw new NoSuchElementException();
+ @SuppressWarnings("unchecked") E result = (E) elements[cursor];
+ // This check doesn't catch all possible comodifications,
+ // but does catch the ones that corrupt traversal
+ if (tail != fence || result == null)
+ throw new ConcurrentModificationException();
+ lastRet = cursor;
+ cursor = (cursor + 1) & (elements.length - 1);
+ return result;
+ }
+
+ public void remove() {
+ if (lastRet < 0)
+ throw new IllegalStateException();
+ if (delete(lastRet)) { // if left-shifted, undo increment in next()
+ cursor = (cursor - 1) & (elements.length - 1);
+ fence = tail;
+ }
+ lastRet = -1;
+ }
+ }
+
+ private class DescendingIterator implements Iterator<E> {
+ /*
+ * This class is nearly a mirror-image of DeqIterator, using
+ * tail instead of head for initial cursor, and head instead of
+ * tail for fence.
+ */
+ private int cursor = tail;
+ private int fence = head;
+ private int lastRet = -1;
+
+ public boolean hasNext() {
+ return cursor != fence;
+ }
+
+ public E next() {
+ if (cursor == fence)
+ throw new NoSuchElementException();
+ cursor = (cursor - 1) & (elements.length - 1);
+ @SuppressWarnings("unchecked") E result = (E) elements[cursor];
+ if (head != fence || result == null)
+ throw new ConcurrentModificationException();
+ lastRet = cursor;
+ return result;
+ }
+
+ public void remove() {
+ if (lastRet < 0)
+ throw new IllegalStateException();
+ if (!delete(lastRet)) {
+ cursor = (cursor + 1) & (elements.length - 1);
+ fence = head;
+ }
+ lastRet = -1;
+ }
+ }
+
+ /**
+ * Returns <tt>true</tt> if this deque contains the specified element.
+ * More formally, returns <tt>true</tt> if and only if this deque contains
+ * at least one element <tt>e</tt> such that <tt>o.equals(e)</tt>.
+ *
+ * @param o object to be checked for containment in this deque
+ * @return <tt>true</tt> if this deque contains the specified element
+ */
+ public boolean contains(Object o) {
+ if (o == null)
+ return false;
+ int mask = elements.length - 1;
+ int i = head;
+ Object x;
+ while ( (x = elements[i]) != null) {
+ if (o.equals(x))
+ return true;
+ i = (i + 1) & mask;
+ }
+ return false;
+ }
+
+ /**
+ * Removes a single instance of the specified element from this deque.
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the first element <tt>e</tt> such that
+ * <tt>o.equals(e)</tt> (if such an element exists).
+ * Returns <tt>true</tt> if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
+ *
+ * <p>This method is equivalent to {@link #removeFirstOccurrence}.
+ *
+ * @param o element to be removed from this deque, if present
+ * @return <tt>true</tt> if this deque contained the specified element
+ */
+ public boolean remove(Object o) {
+ return removeFirstOccurrence(o);
+ }
+
+ /**
+ * Removes all of the elements from this deque.
+ * The deque will be empty after this call returns.
+ */
+ public void clear() {
+ int h = head;
+ int t = tail;
+ if (h != t) { // clear all cells
+ head = tail = 0;
+ int i = h;
+ int mask = elements.length - 1;
+ do {
+ elements[i] = null;
+ i = (i + 1) & mask;
+ } while (i != t);
+ }
+ }
+
+ /**
+ * Returns an array containing all of the elements in this deque
+ * in proper sequence (from first to last element).
+ *
+ * <p>The returned array will be "safe" in that no references to it are
+ * maintained by this deque. (In other words, this method must allocate
+ * a new array). The caller is thus free to modify the returned array.
+ *
+ * <p>This method acts as bridge between array-based and collection-based
+ * APIs.
+ *
+ * @return an array containing all of the elements in this deque
+ */
+ public Object[] toArray() {
+ return copyElements(new Object[size()]);
+ }
+
+ /**
+ * Returns an array containing all of the elements in this deque in
+ * proper sequence (from first to last element); the runtime type of the
+ * returned array is that of the specified array. If the deque fits in
+ * the specified array, it is returned therein. Otherwise, a new array
+ * is allocated with the runtime type of the specified array and the
+ * size of this deque.
+ *
+ * <p>If this deque fits in the specified array with room to spare
+ * (i.e., the array has more elements than this deque), the element in
+ * the array immediately following the end of the deque is set to
+ * <tt>null</tt>.
+ *
+ * <p>Like the {@link #toArray()} method, this method acts as bridge between
+ * array-based and collection-based APIs. Further, this method allows
+ * precise control over the runtime type of the output array, and may,
+ * under certain circumstances, be used to save allocation costs.
+ *
+ * <p>Suppose <tt>x</tt> is a deque known to contain only strings.
+ * The following code can be used to dump the deque into a newly
+ * allocated array of <tt>String</tt>:
+ *
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
+ *
+ * Note that <tt>toArray(new Object[0])</tt> is identical in function to
+ * <tt>toArray()</tt>.
+ *
+ * @param a the array into which the elements of the deque are to
+ * be stored, if it is big enough; otherwise, a new array of the
+ * same runtime type is allocated for this purpose
+ * @return an array containing all of the elements in this deque
+ * @throws ArrayStoreException if the runtime type of the specified array
+ * is not a supertype of the runtime type of every element in
+ * this deque
+ * @throws NullPointerException if the specified array is null
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T[] toArray(T[] a) {
+ int size = size();
+ if (a.length < size)
+ a = (T[])java.lang.reflect.Array.newInstance(
+ a.getClass().getComponentType(), size);
+ copyElements(a);
+ if (a.length > size)
+ a[size] = null;
+ return a;
+ }
+
+ // *** Object methods ***
+
+ /**
+ * Returns a copy of this deque.
+ *
+ * @return a copy of this deque
+ */
+ public ArrayDeque<E> clone() {
+ try {
+ @SuppressWarnings("unchecked")
+ ArrayDeque<E> result = (ArrayDeque<E>) super.clone();
+ result.elements = Arrays.copyOf(elements, elements.length);
+ return result;
+
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Appease the serialization gods.
+ */
+ private static final long serialVersionUID = 2340985798034038923L;
+
+ /**
+ * Serialize this deque.
+ *
+ * @serialData The current size (<tt>int</tt>) of the deque,
+ * followed by all of its elements (each an object reference) in
+ * first-to-last order.
+ */
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+ s.defaultWriteObject();
+
+ // Write out size
+ s.writeInt(size());
+
+ // Write out elements in order.
+ int mask = elements.length - 1;
+ for (int i = head; i != tail; i = (i + 1) & mask)
+ s.writeObject(elements[i]);
+ }
+
+ /**
+ * Deserialize this deque.
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ s.defaultReadObject();
+
+ // Read in size and allocate array
+ int size = s.readInt();
+ allocateElements(size);
+ head = 0;
+ tail = size;
+
+ // Read in all elements in the proper order.
+ for (int i = 0; i < size; i++)
+ elements[i] = s.readObject();
+ }
}
diff --git a/platform/android/viewer/src/com/artifex/mupdfdemo/AsyncTask.java b/platform/android/viewer/src/com/artifex/mupdfdemo/AsyncTask.java
index b370794c..f2dcf70a 100644
--- a/platform/android/viewer/src/com/artifex/mupdfdemo/AsyncTask.java
+++ b/platform/android/viewer/src/com/artifex/mupdfdemo/AsyncTask.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
@@ -67,25 +67,25 @@ import android.os.Message;
* <p>Here is an example of subclassing:</p>
* <pre class="prettyprint">
* private class DownloadFilesTask extends AsyncTask&lt;URL, Integer, Long&gt; {
- * protected Long doInBackground(URL... urls) {
- * int count = urls.length;
- * long totalSize = 0;
- * for (int i = 0; i < count; i++) {
- * totalSize += Downloader.downloadFile(urls[i]);
- * publishProgress((int) ((i / (float) count) * 100));
- * // Escape early if cancel() is called
- * if (isCancelled()) break;
- * }
- * return totalSize;
- * }
+ * protected Long doInBackground(URL... urls) {
+ * int count = urls.length;
+ * long totalSize = 0;
+ * for (int i = 0; i < count; i++) {
+ * totalSize += Downloader.downloadFile(urls[i]);
+ * publishProgress((int) ((i / (float) count) * 100));
+ * // Escape early if cancel() is called
+ * if (isCancelled()) break;
+ * }
+ * return totalSize;
+ * }
*
- * protected void onProgressUpdate(Integer... progress) {
- * setProgressPercent(progress[0]);
- * }
+ * protected void onProgressUpdate(Integer... progress) {
+ * setProgressPercent(progress[0]);
+ * }
*
- * protected void onPostExecute(Long result) {
- * showDialog("Downloaded " + result + " bytes");
- * }
+ * protected void onPostExecute(Long result) {
+ * showDialog("Downloaded " + result + " bytes");
+ * }
* }
* </pre>
*
@@ -97,12 +97,12 @@ import android.os.Message;
* <h2>AsyncTask's generic types</h2>
* <p>The three types used by an asynchronous task are the following:</p>
* <ol>
- * <li><code>Params</code>, the type of the parameters sent to the task upon
- * execution.</li>
- * <li><code>Progress</code>, the type of the progress units published during
- * the background computation.</li>
- * <li><code>Result</code>, the type of the result of the background
- * computation.</li>
+ * <li><code>Params</code>, the type of the parameters sent to the task upon
+ * execution.</li>
+ * <li><code>Progress</code>, the type of the progress units published during
+ * the background computation.</li>
+ * <li><code>Result</code>, the type of the result of the background
+ * computation.</li>
* </ol>
* <p>Not all types are always used by an asynchronous task. To mark a type as unused,
* simply use the type {@link Void}:</p>
@@ -113,25 +113,25 @@ import android.os.Message;
* <h2>The 4 steps</h2>
* <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
* <ol>
- * <li>{@link #onPreExecute()}, invoked on the UI thread before the task
- * is executed. This step is normally used to setup the task, for instance by
- * showing a progress bar in the user interface.</li>
- * <li>{@link #doInBackground}, invoked on the background thread
- * immediately after {@link #onPreExecute()} finishes executing. This step is used
- * to perform background computation that can take a long time. The parameters
- * of the asynchronous task are passed to this step. The result of the computation must
- * be returned by this step and will be passed back to the last step. This step
- * can also use {@link #publishProgress} to publish one or more units
- * of progress. These values are published on the UI thread, in the
- * {@link #onProgressUpdate} step.</li>
- * <li>{@link #onProgressUpdate}, invoked on the UI thread after a
- * call to {@link #publishProgress}. The timing of the execution is
- * undefined. This method is used to display any form of progress in the user
- * interface while the background computation is still executing. For instance,
- * it can be used to animate a progress bar or show logs in a text field.</li>
- * <li>{@link #onPostExecute}, invoked on the UI thread after the background
- * computation finishes. The result of the background computation is passed to
- * this step as a parameter.</li>
+ * <li>{@link #onPreExecute()}, invoked on the UI thread before the task
+ * is executed. This step is normally used to setup the task, for instance by
+ * showing a progress bar in the user interface.</li>
+ * <li>{@link #doInBackground}, invoked on the background thread
+ * immediately after {@link #onPreExecute()} finishes executing. This step is used
+ * to perform background computation that can take a long time. The parameters
+ * of the asynchronous task are passed to this step. The result of the computation must
+ * be returned by this step and will be passed back to the last step. This step
+ * can also use {@link #publishProgress} to publish one or more units
+ * of progress. These values are published on the UI thread, in the
+ * {@link #onProgressUpdate} step.</li>
+ * <li>{@link #onProgressUpdate}, invoked on the UI thread after a
+ * call to {@link #publishProgress}. The timing of the execution is
+ * undefined. This method is used to display any form of progress in the user
+ * interface while the background computation is still executing. For instance,
+ * it can be used to animate a progress bar or show logs in a text field.</li>
+ * <li>{@link #onPostExecute}, invoked on the UI thread after the background
+ * computation finishes. The result of the background computation is passed to
+ * this step as a parameter.</li>
* </ol>
*
* <h2>Cancelling a task</h2>
@@ -147,24 +147,24 @@ import android.os.Message;
* <p>There are a few threading rules that must be followed for this class to
* work properly:</p>
* <ul>
- * <li>The AsyncTask class must be loaded on the UI thread. This is done
- * automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.</li>
- * <li>The task instance must be created on the UI thread.</li>
- * <li>{@link #execute} must be invoked on the UI thread.</li>
- * <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
- * {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
- * <li>The task can be executed only once (an exception will be thrown if
- * a second execution is attempted.)</li>
+ * <li>The AsyncTask class must be loaded on the UI thread. This is done
+ * automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.</li>
+ * <li>The task instance must be created on the UI thread.</li>
+ * <li>{@link #execute} must be invoked on the UI thread.</li>
+ * <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
+ * {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
+ * <li>The task can be executed only once (an exception will be thrown if
+ * a second execution is attempted.)</li>
* </ul>
*
* <h2>Memory observability</h2>
* <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
* operations are safe without explicit synchronizations.</p>
* <ul>
- * <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
- * in {@link #doInBackground}.
- * <li>Set member fields in {@link #doInBackground}, and refer to them in
- * {@link #onProgressUpdate} and {@link #onPostExecute}.
+ * <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
+ * in {@link #doInBackground}.
+ * <li>Set member fields in {@link #doInBackground}, and refer to them in
+ * {@link #onProgressUpdate} and {@link #onPostExecute}.
* </ul>
*
* <h2>Order of execution</h2>
@@ -178,493 +178,493 @@ import android.os.Message;
* {@link #THREAD_POOL_EXECUTOR}.</p>
*/
public abstract class AsyncTask<Params, Progress, Result> {
- private static final String LOG_TAG = "AsyncTask";
-
- private static final int CORE_POOL_SIZE = 5;
- private static final int MAXIMUM_POOL_SIZE = 128;
- private static final int KEEP_ALIVE = 1;
-
- private static final ThreadFactory sThreadFactory = new ThreadFactory() {
- private final AtomicInteger mCount = new AtomicInteger(1);
-
- public Thread newThread(Runnable r) {
- return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
- }
- };
-
- private static final BlockingQueue<Runnable> sPoolWorkQueue =
- new LinkedBlockingQueue<Runnable>(10);
-
- /**
- * An {@link Executor} that can be used to execute tasks in parallel.
- */
- public static final Executor THREAD_POOL_EXECUTOR
- = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
- TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
-
- /**
- * An {@link Executor} that executes tasks one at a time in serial
- * order. This serialization is global to a particular process.
- */
- public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
-
- private static final int MESSAGE_POST_RESULT = 0x1;
- private static final int MESSAGE_POST_PROGRESS = 0x2;
-
- private static final InternalHandler sHandler = new InternalHandler();
-
- private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
- private final WorkerRunnable<Params, Result> mWorker;
- private final FutureTask<Result> mFuture;
-
- private volatile Status mStatus = Status.PENDING;
-
- private final AtomicBoolean mCancelled = new AtomicBoolean();
- private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
-
- private static class SerialExecutor implements Executor {
- final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
- Runnable mActive;
-
- public synchronized void execute(final Runnable r) {
- mTasks.offer(new Runnable() {
- public void run() {
- try {
- r.run();
- } finally {
- scheduleNext();
- }
- }
- });
- if (mActive == null) {
- scheduleNext();
- }
- }
-
- protected synchronized void scheduleNext() {
- if ((mActive = mTasks.poll()) != null) {
- THREAD_POOL_EXECUTOR.execute(mActive);
- }
- }
- }
-
- /**
- * Indicates the current status of the task. Each status will be set only once
- * during the lifetime of a task.
- */
- public enum Status {
- /**
- * Indicates that the task has not been executed yet.
- */
- PENDING,
- /**
- * Indicates that the task is running.
- */
- RUNNING,
- /**
- * Indicates that {@link AsyncTask#onPostExecute} has finished.
- */
- FINISHED,
- }
-
- /** @hide Used to force static handler to be created. */
- public static void init() {
- sHandler.getLooper();
- }
-
- /** @hide */
- public static void setDefaultExecutor(Executor exec) {
- sDefaultExecutor = exec;
- }
-
- /**
- * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
- */
- public AsyncTask() {
- mWorker = new WorkerRunnable<Params, Result>() {
- public Result call() throws Exception {
- mTaskInvoked.set(true);
-
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- //noinspection unchecked
- return postResult(doInBackground(mParams));
- }
- };
-
- mFuture = new FutureTask<Result>(mWorker) {
- @Override
- protected void done() {
- try {
- postResultIfNotInvoked(get());
- } catch (InterruptedException e) {
- android.util.Log.w(LOG_TAG, e);
- } catch (ExecutionException e) {
- throw new RuntimeException("An error occured while executing doInBackground()",
- e.getCause());
- } catch (CancellationException e) {
- postResultIfNotInvoked(null);
- }
- }
- };
- }
-
- private void postResultIfNotInvoked(Result result) {
- final boolean wasTaskInvoked = mTaskInvoked.get();
- if (!wasTaskInvoked) {
- postResult(result);
- }
- }
-
- private Result postResult(Result result) {
- @SuppressWarnings("unchecked")
- Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
- new AsyncTaskResult<Result>(this, result));
- message.sendToTarget();
- return result;
- }
-
- /**
- * Returns the current status of this task.
- *
- * @return The current status.
- */
- public final Status getStatus() {
- return mStatus;
- }
-
- /**
- * Override this method to perform a computation on a background thread. The
- * specified parameters are the parameters passed to {@link #execute}
- * by the caller of this task.
- *
- * This method can call {@link #publishProgress} to publish updates
- * on the UI thread.
- *
- * @param params The parameters of the task.
- *
- * @return A result, defined by the subclass of this task.
- *
- * @see #onPreExecute()
- * @see #onPostExecute
- * @see #publishProgress
- */
- protected abstract Result doInBackground(Params... params);
-
- /**
- * Runs on the UI thread before {@link #doInBackground}.
- *
- * @see #onPostExecute
- * @see #doInBackground
- */
- protected void onPreExecute() {
- }
-
- /**
- * <p>Runs on the UI thread after {@link #doInBackground}. The
- * specified result is the value returned by {@link #doInBackground}.</p>
- *
- * <p>This method won't be invoked if the task was cancelled.</p>
- *
- * @param result The result of the operation computed by {@link #doInBackground}.
- *
- * @see #onPreExecute
- * @see #doInBackground
- * @see #onCancelled(Object)
- */
- @SuppressWarnings({"UnusedDeclaration"})
- protected void onPostExecute(Result result) {
- }
-
- /**
- * Runs on the UI thread after {@link #publishProgress} is invoked.
- * The specified values are the values passed to {@link #publishProgress}.
- *
- * @param values The values indicating progress.
- *
- * @see #publishProgress
- * @see #doInBackground
- */
- @SuppressWarnings({"UnusedDeclaration"})
- protected void onProgressUpdate(Progress... values) {
- }
-
- /**
- * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
- * {@link #doInBackground(Object[])} has finished.</p>
- *
- * <p>The default implementation simply invokes {@link #onCancelled()} and
- * ignores the result. If you write your own implementation, do not call
- * <code>super.onCancelled(result)</code>.</p>
- *
- * @param result The result, if any, computed in
- * {@link #doInBackground(Object[])}, can be null
- *
- * @see #cancel(boolean)
- * @see #isCancelled()
- */
- @SuppressWarnings({"UnusedParameters"})
- protected void onCancelled(Result result) {
- onCancelled();
- }
-
- /**
- * <p>Applications should preferably override {@link #onCancelled(Object)}.
- * This method is invoked by the default implementation of
- * {@link #onCancelled(Object)}.</p>
- *
- * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
- * {@link #doInBackground(Object[])} has finished.</p>
- *
- * @see #onCancelled(Object)
- * @see #cancel(boolean)
- * @see #isCancelled()
- */
- protected void onCancelled() {
- }
-
- /**
- * Returns <tt>true</tt> if this task was cancelled before it completed
- * normally. If you are calling {@link #cancel(boolean)} on the task,
- * the value returned by this method should be checked periodically from
- * {@link #doInBackground(Object[])} to end the task as soon as possible.
- *
- * @return <tt>true</tt> if task was cancelled before it completed
- *
- * @see #cancel(boolean)
- */
- public final boolean isCancelled() {
- return mCancelled.get();
- }
-
- /**
- * <p>Attempts to cancel execution of this task. This attempt will
- * fail if the task has already completed, already been cancelled,
- * or could not be cancelled for some other reason. If successful,
- * and this task has not started when <tt>cancel</tt> is called,
- * this task should never run. If the task has already started,
- * then the <tt>mayInterruptIfRunning</tt> parameter determines
- * whether the thread executing this task should be interrupted in
- * an attempt to stop the task.</p>
- *
- * <p>Calling this method will result in {@link #onCancelled(Object)} being
- * invoked on the UI thread after {@link #doInBackground(Object[])}
- * returns. Calling this method guarantees that {@link #onPostExecute(Object)}
- * is never invoked. After invoking this method, you should check the
- * value returned by {@link #isCancelled()} periodically from
- * {@link #doInBackground(Object[])} to finish the task as early as
- * possible.</p>
- *
- * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
- * task should be interrupted; otherwise, in-progress tasks are allowed
- * to complete.
- *
- * @return <tt>false</tt> if the task could not be cancelled,
- * typically because it has already completed normally;
- * <tt>true</tt> otherwise
- *
- * @see #isCancelled()
- * @see #onCancelled(Object)
- */
- public final boolean cancel(boolean mayInterruptIfRunning) {
- mCancelled.set(true);
- return mFuture.cancel(mayInterruptIfRunning);
- }
-
- /**
- * Waits if necessary for the computation to complete, and then
- * retrieves its result.
- *
- * @return The computed result.
- *
- * @throws CancellationException If the computation was cancelled.
- * @throws ExecutionException If the computation threw an exception.
- * @throws InterruptedException If the current thread was interrupted
- * while waiting.
- */
- public final Result get() throws InterruptedException, ExecutionException {
- return mFuture.get();
- }
-
- /**
- * Waits if necessary for at most the given time for the computation
- * to complete, and then retrieves its result.
- *
- * @param timeout Time to wait before cancelling the operation.
- * @param unit The time unit for the timeout.
- *
- * @return The computed result.
- *
- * @throws CancellationException If the computation was cancelled.
- * @throws ExecutionException If the computation threw an exception.
- * @throws InterruptedException If the current thread was interrupted
- * while waiting.
- * @throws TimeoutException If the wait timed out.
- */
- public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
- ExecutionException, TimeoutException {
- return mFuture.get(timeout, unit);
- }
-
- /**
- * Executes the task with the specified parameters. The task returns
- * itself (this) so that the caller can keep a reference to it.
- *
- * <p>Note: this function schedules the task on a queue for a single background
- * thread or pool of threads depending on the platform version. When first
- * introduced, AsyncTasks were executed serially on a single background thread.
- * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
- * to a pool of threads allowing multiple tasks to operate in parallel. Starting
- * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being
- * executed on a single thread to avoid common application errors caused
- * by parallel execution. If you truly want parallel execution, you can use
- * the {@link #executeOnExecutor} version of this method
- * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings
- * on its use.
- *
- * <p>This method must be invoked on the UI thread.
- *
- * @param params The parameters of the task.
- *
- * @return This instance of AsyncTask.
- *
- * @throws IllegalStateException If {@link #getStatus()} returns either
- * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
- *
- * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
- * @see #execute(Runnable)
- */
- public final AsyncTask<Params, Progress, Result> execute(Params... params) {
- return executeOnExecutor(sDefaultExecutor, params);
- }
-
- /**
- * Executes the task with the specified parameters. The task returns
- * itself (this) so that the caller can keep a reference to it.
- *
- * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
- * allow multiple tasks to run in parallel on a pool of threads managed by
- * AsyncTask, however you can also use your own {@link Executor} for custom
- * behavior.
- *
- * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
- * a thread pool is generally <em>not</em> what one wants, because the order
- * of their operation is not defined. For example, if these tasks are used
- * to modify any state in common (such as writing a file due to a button click),
- * there are no guarantees on the order of the modifications.
- * Without careful work it is possible in rare cases for the newer version
- * of the data to be over-written by an older one, leading to obscure data
- * loss and stability issues. Such changes are best
- * executed in serial; to guarantee such work is serialized regardless of
- * platform version you can use this function with {@link #SERIAL_EXECUTOR}.
- *
- * <p>This method must be invoked on the UI thread.
- *
- * @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a
- * convenient process-wide thread pool for tasks that are loosely coupled.
- * @param params The parameters of the task.
- *
- * @return This instance of AsyncTask.
- *
- * @throws IllegalStateException If {@link #getStatus()} returns either
- * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
- *
- * @see #execute(Object[])
- */
- public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
- Params... params) {
- if (mStatus != Status.PENDING) {
- switch (mStatus) {
- case RUNNING:
- throw new IllegalStateException("Cannot execute task:"
- + " the task is already running.");
- case FINISHED:
- throw new IllegalStateException("Cannot execute task:"
- + " the task has already been executed "
- + "(a task can be executed only once)");
- }
- }
-
- mStatus = Status.RUNNING;
-
- onPreExecute();
-
- mWorker.mParams = params;
- exec.execute(mFuture);
-
- return this;
- }
-
- /**
- * Convenience version of {@link #execute(Object...)} for use with
- * a simple Runnable object. See {@link #execute(Object[])} for more
- * information on the order of execution.
- *
- * @see #execute(Object[])
- * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
- */
- public static void execute(Runnable runnable) {
- sDefaultExecutor.execute(runnable);
- }
-
- /**
- * This method can be invoked from {@link #doInBackground} to
- * publish updates on the UI thread while the background computation is
- * still running. Each call to this method will trigger the execution of
- * {@link #onProgressUpdate} on the UI thread.
- *
- * {@link #onProgressUpdate} will note be called if the task has been
- * canceled.
- *
- * @param values The progress values to update the UI with.
- *
- * @see #onProgressUpdate
- * @see #doInBackground
- */
- protected final void publishProgress(Progress... values) {
- if (!isCancelled()) {
- sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
- new AsyncTaskResult<Progress>(this, values)).sendToTarget();
- }
- }
-
- private void finish(Result result) {
- if (isCancelled()) {
- onCancelled(result);
- } else {
- onPostExecute(result);
- }
- mStatus = Status.FINISHED;
- }
-
- private static class InternalHandler extends Handler {
- @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
- @Override
- public void handleMessage(Message msg) {
- AsyncTaskResult result = (AsyncTaskResult) msg.obj;
- switch (msg.what) {
- case MESSAGE_POST_RESULT:
- // There is only one result
- result.mTask.finish(result.mData[0]);
- break;
- case MESSAGE_POST_PROGRESS:
- result.mTask.onProgressUpdate(result.mData);
- break;
- }
- }
- }
-
- private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
- Params[] mParams;
- }
-
- @SuppressWarnings({"RawUseOfParameterizedType"})
- private static class AsyncTaskResult<Data> {
- final AsyncTask mTask;
- final Data[] mData;
-
- AsyncTaskResult(AsyncTask task, Data... data) {
- mTask = task;
- mData = data;
- }
- }
+ private static final String LOG_TAG = "AsyncTask";
+
+ private static final int CORE_POOL_SIZE = 5;
+ private static final int MAXIMUM_POOL_SIZE = 128;
+ private static final int KEEP_ALIVE = 1;
+
+ private static final ThreadFactory sThreadFactory = new ThreadFactory() {
+ private final AtomicInteger mCount = new AtomicInteger(1);
+
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
+ }
+ };
+
+ private static final BlockingQueue<Runnable> sPoolWorkQueue =
+ new LinkedBlockingQueue<Runnable>(10);
+
+ /**
+ * An {@link Executor} that can be used to execute tasks in parallel.
+ */
+ public static final Executor THREAD_POOL_EXECUTOR
+ = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
+ TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
+
+ /**
+ * An {@link Executor} that executes tasks one at a time in serial
+ * order. This serialization is global to a particular process.
+ */
+ public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
+
+ private static final int MESSAGE_POST_RESULT = 0x1;
+ private static final int MESSAGE_POST_PROGRESS = 0x2;
+
+ private static final InternalHandler sHandler = new InternalHandler();
+
+ private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
+ private final WorkerRunnable<Params, Result> mWorker;
+ private final FutureTask<Result> mFuture;
+
+ private volatile Status mStatus = Status.PENDING;
+
+ private final AtomicBoolean mCancelled = new AtomicBoolean();
+ private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
+
+ private static class SerialExecutor implements Executor {
+ final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
+ Runnable mActive;
+
+ public synchronized void execute(final Runnable r) {
+ mTasks.offer(new Runnable() {
+ public void run() {
+ try {
+ r.run();
+ } finally {
+ scheduleNext();
+ }
+ }
+ });
+ if (mActive == null) {
+ scheduleNext();
+ }
+ }
+
+ protected synchronized void scheduleNext() {
+ if ((mActive = mTasks.poll()) != null) {
+ THREAD_POOL_EXECUTOR.execute(mActive);
+ }
+ }
+ }
+
+ /**
+ * Indicates the current status of the task. Each status will be set only once
+ * during the lifetime of a task.
+ */
+ public enum Status {
+ /**
+ * Indicates that the task has not been executed yet.
+ */
+ PENDING,
+ /**
+ * Indicates that the task is running.
+ */
+ RUNNING,
+ /**
+ * Indicates that {@link AsyncTask#onPostExecute} has finished.
+ */
+ FINISHED,
+ }
+
+ /** @hide Used to force static handler to be created. */
+ public static void init() {
+ sHandler.getLooper();
+ }
+
+ /** @hide */
+ public static void setDefaultExecutor(Executor exec) {
+ sDefaultExecutor = exec;
+ }
+
+ /**
+ * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
+ */
+ public AsyncTask() {
+ mWorker = new WorkerRunnable<Params, Result>() {
+ public Result call() throws Exception {
+ mTaskInvoked.set(true);
+
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ //noinspection unchecked
+ return postResult(doInBackground(mParams));
+ }
+ };
+
+ mFuture = new FutureTask<Result>(mWorker) {
+ @Override
+ protected void done() {
+ try {
+ postResultIfNotInvoked(get());
+ } catch (InterruptedException e) {
+ android.util.Log.w(LOG_TAG, e);
+ } catch (ExecutionException e) {
+ throw new RuntimeException("An error occured while executing doInBackground()",
+ e.getCause());
+ } catch (CancellationException e) {
+ postResultIfNotInvoked(null);
+ }
+ }
+ };
+ }
+
+ private void postResultIfNotInvoked(Result result) {
+ final boolean wasTaskInvoked = mTaskInvoked.get();
+ if (!wasTaskInvoked) {
+ postResult(result);
+ }
+ }
+
+ private Result postResult(Result result) {
+ @SuppressWarnings("unchecked")
+ Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
+ new AsyncTaskResult<Result>(this, result));
+ message.sendToTarget();
+ return result;
+ }
+
+ /**
+ * Returns the current status of this task.
+ *
+ * @return The current status.
+ */
+ public final Status getStatus() {
+ return mStatus;
+ }
+
+ /**
+ * Override this method to perform a computation on a background thread. The
+ * specified parameters are the parameters passed to {@link #execute}
+ * by the caller of this task.
+ *
+ * This method can call {@link #publishProgress} to publish updates
+ * on the UI thread.
+ *
+ * @param params The parameters of the task.
+ *
+ * @return A result, defined by the subclass of this task.
+ *
+ * @see #onPreExecute()
+ * @see #onPostExecute
+ * @see #publishProgress
+ */
+ protected abstract Result doInBackground(Params... params);
+
+ /**
+ * Runs on the UI thread before {@link #doInBackground}.
+ *
+ * @see #onPostExecute
+ * @see #doInBackground
+ */
+ protected void onPreExecute() {
+ }
+
+ /**
+ * <p>Runs on the UI thread after {@link #doInBackground}. The
+ * specified result is the value returned by {@link #doInBackground}.</p>
+ *
+ * <p>This method won't be invoked if the task was cancelled.</p>
+ *
+ * @param result The result of the operation computed by {@link #doInBackground}.
+ *
+ * @see #onPreExecute
+ * @see #doInBackground
+ * @see #onCancelled(Object)
+ */
+ @SuppressWarnings({"UnusedDeclaration"})
+ protected void onPostExecute(Result result) {
+ }
+
+ /**
+ * Runs on the UI thread after {@link #publishProgress} is invoked.
+ * The specified values are the values passed to {@link #publishProgress}.
+ *
+ * @param values The values indicating progress.
+ *
+ * @see #publishProgress
+ * @see #doInBackground
+ */
+ @SuppressWarnings({"UnusedDeclaration"})
+ protected void onProgressUpdate(Progress... values) {
+ }
+
+ /**
+ * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
+ * {@link #doInBackground(Object[])} has finished.</p>
+ *
+ * <p>The default implementation simply invokes {@link #onCancelled()} and
+ * ignores the result. If you write your own implementation, do not call
+ * <code>super.onCancelled(result)</code>.</p>
+ *
+ * @param result The result, if any, computed in
+ * {@link #doInBackground(Object[])}, can be null
+ *
+ * @see #cancel(boolean)
+ * @see #isCancelled()
+ */
+ @SuppressWarnings({"UnusedParameters"})
+ protected void onCancelled(Result result) {
+ onCancelled();
+ }
+
+ /**
+ * <p>Applications should preferably override {@link #onCancelled(Object)}.
+ * This method is invoked by the default implementation of
+ * {@link #onCancelled(Object)}.</p>
+ *
+ * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
+ * {@link #doInBackground(Object[])} has finished.</p>
+ *
+ * @see #onCancelled(Object)
+ * @see #cancel(boolean)
+ * @see #isCancelled()
+ */
+ protected void onCancelled() {
+ }
+
+ /**
+ * Returns <tt>true</tt> if this task was cancelled before it completed
+ * normally. If you are calling {@link #cancel(boolean)} on the task,
+ * the value returned by this method should be checked periodically from
+ * {@link #doInBackground(Object[])} to end the task as soon as possible.
+ *
+ * @return <tt>true</tt> if task was cancelled before it completed
+ *
+ * @see #cancel(boolean)
+ */
+ public final boolean isCancelled() {
+ return mCancelled.get();
+ }
+
+ /**
+ * <p>Attempts to cancel execution of this task. This attempt will
+ * fail if the task has already completed, already been cancelled,
+ * or could not be cancelled for some other reason. If successful,
+ * and this task has not started when <tt>cancel</tt> is called,
+ * this task should never run. If the task has already started,
+ * then the <tt>mayInterruptIfRunning</tt> parameter determines
+ * whether the thread executing this task should be interrupted in
+ * an attempt to stop the task.</p>
+ *
+ * <p>Calling this method will result in {@link #onCancelled(Object)} being
+ * invoked on the UI thread after {@link #doInBackground(Object[])}
+ * returns. Calling this method guarantees that {@link #onPostExecute(Object)}
+ * is never invoked. After invoking this method, you should check the
+ * value returned by {@link #isCancelled()} periodically from
+ * {@link #doInBackground(Object[])} to finish the task as early as
+ * possible.</p>
+ *
+ * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
+ * task should be interrupted; otherwise, in-progress tasks are allowed
+ * to complete.
+ *
+ * @return <tt>false</tt> if the task could not be cancelled,
+ * typically because it has already completed normally;
+ * <tt>true</tt> otherwise
+ *
+ * @see #isCancelled()
+ * @see #onCancelled(Object)
+ */
+ public final boolean cancel(boolean mayInterruptIfRunning) {
+ mCancelled.set(true);
+ return mFuture.cancel(mayInterruptIfRunning);
+ }
+
+ /**
+ * Waits if necessary for the computation to complete, and then
+ * retrieves its result.
+ *
+ * @return The computed result.
+ *
+ * @throws CancellationException If the computation was cancelled.
+ * @throws ExecutionException If the computation threw an exception.
+ * @throws InterruptedException If the current thread was interrupted
+ * while waiting.
+ */
+ public final Result get() throws InterruptedException, ExecutionException {
+ return mFuture.get();
+ }
+
+ /**
+ * Waits if necessary for at most the given time for the computation
+ * to complete, and then retrieves its result.
+ *
+ * @param timeout Time to wait before cancelling the operation.
+ * @param unit The time unit for the timeout.
+ *
+ * @return The computed result.
+ *
+ * @throws CancellationException If the computation was cancelled.
+ * @throws ExecutionException If the computation threw an exception.
+ * @throws InterruptedException If the current thread was interrupted
+ * while waiting.
+ * @throws TimeoutException If the wait timed out.
+ */
+ public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
+ ExecutionException, TimeoutException {
+ return mFuture.get(timeout, unit);
+ }
+
+ /**
+ * Executes the task with the specified parameters. The task returns
+ * itself (this) so that the caller can keep a reference to it.
+ *
+ * <p>Note: this function schedules the task on a queue for a single background
+ * thread or pool of threads depending on the platform version. When first
+ * introduced, AsyncTasks were executed serially on a single background thread.
+ * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
+ * to a pool of threads allowing multiple tasks to operate in parallel. Starting
+ * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being
+ * executed on a single thread to avoid common application errors caused
+ * by parallel execution. If you truly want parallel execution, you can use
+ * the {@link #executeOnExecutor} version of this method
+ * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings
+ * on its use.
+ *
+ * <p>This method must be invoked on the UI thread.
+ *
+ * @param params The parameters of the task.
+ *
+ * @return This instance of AsyncTask.
+ *
+ * @throws IllegalStateException If {@link #getStatus()} returns either
+ * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
+ *
+ * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
+ * @see #execute(Runnable)
+ */
+ public final AsyncTask<Params, Progress, Result> execute(Params... params) {
+ return executeOnExecutor(sDefaultExecutor, params);
+ }
+
+ /**
+ * Executes the task with the specified parameters. The task returns
+ * itself (this) so that the caller can keep a reference to it.
+ *
+ * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
+ * allow multiple tasks to run in parallel on a pool of threads managed by
+ * AsyncTask, however you can also use your own {@link Executor} for custom
+ * behavior.
+ *
+ * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
+ * a thread pool is generally <em>not</em> what one wants, because the order
+ * of their operation is not defined. For example, if these tasks are used
+ * to modify any state in common (such as writing a file due to a button click),
+ * there are no guarantees on the order of the modifications.
+ * Without careful work it is possible in rare cases for the newer version
+ * of the data to be over-written by an older one, leading to obscure data
+ * loss and stability issues. Such changes are best
+ * executed in serial; to guarantee such work is serialized regardless of
+ * platform version you can use this function with {@link #SERIAL_EXECUTOR}.
+ *
+ * <p>This method must be invoked on the UI thread.
+ *
+ * @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a
+ * convenient process-wide thread pool for tasks that are loosely coupled.
+ * @param params The parameters of the task.
+ *
+ * @return This instance of AsyncTask.
+ *
+ * @throws IllegalStateException If {@link #getStatus()} returns either
+ * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
+ *
+ * @see #execute(Object[])
+ */
+ public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
+ Params... params) {
+ if (mStatus != Status.PENDING) {
+ switch (mStatus) {
+ case RUNNING:
+ throw new IllegalStateException("Cannot execute task:"
+ + " the task is already running.");
+ case FINISHED:
+ throw new IllegalStateException("Cannot execute task:"
+ + " the task has already been executed "
+ + "(a task can be executed only once)");
+ }
+ }
+
+ mStatus = Status.RUNNING;
+
+ onPreExecute();
+
+ mWorker.mParams = params;
+ exec.execute(mFuture);
+
+ return this;
+ }
+
+ /**
+ * Convenience version of {@link #execute(Object...)} for use with
+ * a simple Runnable object. See {@link #execute(Object[])} for more
+ * information on the order of execution.
+ *
+ * @see #execute(Object[])
+ * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
+ */
+ public static void execute(Runnable runnable) {
+ sDefaultExecutor.execute(runnable);
+ }
+
+ /**
+ * This method can be invoked from {@link #doInBackground} to
+ * publish updates on the UI thread while the background computation is
+ * still running. Each call to this method will trigger the execution of
+ * {@link #onProgressUpdate} on the UI thread.
+ *
+ * {@link #onProgressUpdate} will note be called if the task has been
+ * canceled.
+ *
+ * @param values The progress values to update the UI with.
+ *
+ * @see #onProgressUpdate
+ * @see #doInBackground
+ */
+ protected final void publishProgress(Progress... values) {
+ if (!isCancelled()) {
+ sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
+ new AsyncTaskResult<Progress>(this, values)).sendToTarget();
+ }
+ }
+
+ private void finish(Result result) {
+ if (isCancelled()) {
+ onCancelled(result);
+ } else {
+ onPostExecute(result);
+ }
+ mStatus = Status.FINISHED;
+ }
+
+ private static class InternalHandler extends Handler {
+ @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncTaskResult result = (AsyncTaskResult) msg.obj;
+ switch (msg.what) {
+ case MESSAGE_POST_RESULT:
+ // There is only one result
+ result.mTask.finish(result.mData[0]);
+ break;
+ case MESSAGE_POST_PROGRESS:
+ result.mTask.onProgressUpdate(result.mData);
+ break;
+ }
+ }
+ }
+
+ private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
+ Params[] mParams;
+ }
+
+ @SuppressWarnings({"RawUseOfParameterizedType"})
+ private static class AsyncTaskResult<Data> {
+ final AsyncTask mTask;
+ final Data[] mData;
+
+ AsyncTaskResult(AsyncTask task, Data... data) {
+ mTask = task;
+ mData = data;
+ }
+ }
}
diff --git a/platform/android/viewer/src/com/artifex/mupdfdemo/Deque.java b/platform/android/viewer/src/com/artifex/mupdfdemo/Deque.java
index ad17bcc0..97aefeab 100644
--- a/platform/android/viewer/src/com/artifex/mupdfdemo/Deque.java
+++ b/platform/android/viewer/src/com/artifex/mupdfdemo/Deque.java
@@ -41,37 +41,37 @@ import java.util.Stack;
* <p>
* <table BORDER CELLPADDING=3 CELLSPACING=1>
* <tr>
- * <td></td>
- * <td ALIGN=CENTER COLSPAN = 2> <b>First Element (Head)</b></td>
- * <td ALIGN=CENTER COLSPAN = 2> <b>Last Element (Tail)</b></td>
+ * <td></td>
+ * <td ALIGN=CENTER COLSPAN = 2> <b>First Element (Head)</b></td>
+ * <td ALIGN=CENTER COLSPAN = 2> <b>Last Element (Tail)</b></td>
* </tr>
* <tr>
- * <td></td>
- * <td ALIGN=CENTER><em>Throws exception</em></td>
- * <td ALIGN=CENTER><em>Special value</em></td>
- * <td ALIGN=CENTER><em>Throws exception</em></td>
- * <td ALIGN=CENTER><em>Special value</em></td>
+ * <td></td>
+ * <td ALIGN=CENTER><em>Throws exception</em></td>
+ * <td ALIGN=CENTER><em>Special value</em></td>
+ * <td ALIGN=CENTER><em>Throws exception</em></td>
+ * <td ALIGN=CENTER><em>Special value</em></td>
* </tr>
* <tr>
- * <td><b>Insert</b></td>
- * <td>{@link #addFirst addFirst(e)}</td>
- * <td>{@link #offerFirst offerFirst(e)}</td>
- * <td>{@link #addLast addLast(e)}</td>
- * <td>{@link #offerLast offerLast(e)}</td>
+ * <td><b>Insert</b></td>
+ * <td>{@link #addFirst addFirst(e)}</td>
+ * <td>{@link #offerFirst offerFirst(e)}</td>
+ * <td>{@link #addLast addLast(e)}</td>
+ * <td>{@link #offerLast offerLast(e)}</td>
* </tr>
* <tr>
- * <td><b>Remove</b></td>
- * <td>{@link #removeFirst removeFirst()}</td>
- * <td>{@link #pollFirst pollFirst()}</td>
- * <td>{@link #removeLast removeLast()}</td>
- * <td>{@link #pollLast pollLast()}</td>
+ * <td><b>Remove</b></td>
+ * <td>{@link #removeFirst removeFirst()}</td>
+ * <td>{@link #pollFirst pollFirst()}</td>
+ * <td>{@link #removeLast removeLast()}</td>
+ * <td>{@link #pollLast pollLast()}</td>
* </tr>
* <tr>
- * <td><b>Examine</b></td>
- * <td>{@link #getFirst getFirst()}</td>
- * <td>{@link #peekFirst peekFirst()}</td>
- * <td>{@link #getLast getLast()}</td>
- * <td>{@link #peekLast peekLast()}</td>
+ * <td><b>Examine</b></td>
+ * <td>{@link #getFirst getFirst()}</td>
+ * <td>{@link #peekFirst peekFirst()}</td>
+ * <td>{@link #getLast getLast()}</td>
+ * <td>{@link #peekLast peekLast()}</td>
* </tr>
* </table>
*
@@ -84,32 +84,32 @@ import java.util.Stack;
* <p>
* <table BORDER CELLPADDING=3 CELLSPACING=1>
* <tr>
- * <td ALIGN=CENTER> <b><tt>Queue</tt> Method</b></td>
- * <td ALIGN=CENTER> <b>Equivalent <tt>Deque</tt> Method</b></td>
+ * <td ALIGN=CENTER> <b><tt>Queue</tt> Method</b></td>
+ * <td ALIGN=CENTER> <b>Equivalent <tt>Deque</tt> Method</b></td>
* </tr>
* <tr>
- * <td>{@link java.util.Queue#add add(e)}</td>
- * <td>{@link #addLast addLast(e)}</td>
+ * <td>{@link java.util.Queue#add add(e)}</td>
+ * <td>{@link #addLast addLast(e)}</td>
* </tr>
* <tr>
- * <td>{@link java.util.Queue#offer offer(e)}</td>
- * <td>{@link #offerLast offerLast(e)}</td>
+ * <td>{@link java.util.Queue#offer offer(e)}</td>
+ * <td>{@link #offerLast offerLast(e)}</td>
* </tr>
* <tr>
- * <td>{@link java.util.Queue#remove remove()}</td>
- * <td>{@link #removeFirst removeFirst()}</td>
+ * <td>{@link java.util.Queue#remove remove()}</td>
+ * <td>{@link #removeFirst removeFirst()}</td>
* </tr>
* <tr>
- * <td>{@link java.util.Queue#poll poll()}</td>
- * <td>{@link #pollFirst pollFirst()}</td>
+ * <td>{@link java.util.Queue#poll poll()}</td>
+ * <td>{@link #pollFirst pollFirst()}</td>
* </tr>
* <tr>
- * <td>{@link java.util.Queue#element element()}</td>
- * <td>{@link #getFirst getFirst()}</td>
+ * <td>{@link java.util.Queue#element element()}</td>
+ * <td>{@link #getFirst getFirst()}</td>
* </tr>
* <tr>
- * <td>{@link java.util.Queue#peek peek()}</td>
- * <td>{@link #peek peekFirst()}</td>
+ * <td>{@link java.util.Queue#peek peek()}</td>
+ * <td>{@link #peek peekFirst()}</td>
* </tr>
* </table>
*
@@ -122,20 +122,20 @@ import java.util.Stack;
* <p>
* <table BORDER CELLPADDING=3 CELLSPACING=1>
* <tr>
- * <td ALIGN=CENTER> <b>Stack Method</b></td>
- * <td ALIGN=CENTER> <b>Equivalent <tt>Deque</tt> Method</b></td>
+ * <td ALIGN=CENTER> <b>Stack Method</b></td>
+ * <td ALIGN=CENTER> <b>Equivalent <tt>Deque</tt> Method</b></td>
* </tr>
* <tr>
- * <td>{@link #push push(e)}</td>
- * <td>{@link #addFirst addFirst(e)}</td>
+ * <td>{@link #push push(e)}</td>
+ * <td>{@link #addFirst addFirst(e)}</td>
* </tr>
* <tr>
- * <td>{@link #pop pop()}</td>
- * <td>{@link #removeFirst removeFirst()}</td>
+ * <td>{@link #pop pop()}</td>
+ * <td>{@link #removeFirst removeFirst()}</td>
* </tr>
* <tr>
- * <td>{@link #peek peek()}</td>
- * <td>{@link #peekFirst peekFirst()}</td>
+ * <td>{@link #peek peek()}</td>
+ * <td>{@link #peekFirst peekFirst()}</td>
* </tr>
* </table>
*
@@ -170,383 +170,383 @@ import java.util.Stack;
*/
public interface Deque<E> extends Queue<E> {
- /**
- * Inserts the specified element at the front of this deque if it is
- * possible to do so immediately without violating capacity restrictions.
- * When using a capacity-restricted deque, it is generally preferable to
- * use method {@link #offerFirst}.
- *
- * @param e the element to add
- * @throws IllegalStateException if the element cannot be added at this
- * time due to capacity restrictions
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- void addFirst(E e);
-
- /**
- * Inserts the specified element at the end of this deque if it is
- * possible to do so immediately without violating capacity restrictions.
- * When using a capacity-restricted deque, it is generally preferable to
- * use method {@link #offerLast}.
- *
- * <p>This method is equivalent to {@link #add}.
- *
- * @param e the element to add
- * @throws IllegalStateException if the element cannot be added at this
- * time due to capacity restrictions
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- void addLast(E e);
-
- /**
- * Inserts the specified element at the front of this deque unless it would
- * violate capacity restrictions. When using a capacity-restricted deque,
- * this method is generally preferable to the {@link #addFirst} method,
- * which can fail to insert an element only by throwing an exception.
- *
- * @param e the element to add
- * @return <tt>true</tt> if the element was added to this deque, else
- * <tt>false</tt>
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- boolean offerFirst(E e);
-
- /**
- * Inserts the specified element at the end of this deque unless it would
- * violate capacity restrictions. When using a capacity-restricted deque,
- * this method is generally preferable to the {@link #addLast} method,
- * which can fail to insert an element only by throwing an exception.
- *
- * @param e the element to add
- * @return <tt>true</tt> if the element was added to this deque, else
- * <tt>false</tt>
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- boolean offerLast(E e);
-
- /**
- * Retrieves and removes the first element of this deque. This method
- * differs from {@link #pollFirst pollFirst} only in that it throws an
- * exception if this deque is empty.
- *
- * @return the head of this deque
- * @throws NoSuchElementException if this deque is empty
- */
- E removeFirst();
-
- /**
- * Retrieves and removes the last element of this deque. This method
- * differs from {@link #pollLast pollLast} only in that it throws an
- * exception if this deque is empty.
- *
- * @return the tail of this deque
- * @throws NoSuchElementException if this deque is empty
- */
- E removeLast();
-
- /**
- * Retrieves and removes the first element of this deque,
- * or returns <tt>null</tt> if this deque is empty.
- *
- * @return the head of this deque, or <tt>null</tt> if this deque is empty
- */
- E pollFirst();
-
- /**
- * Retrieves and removes the last element of this deque,
- * or returns <tt>null</tt> if this deque is empty.
- *
- * @return the tail of this deque, or <tt>null</tt> if this deque is empty
- */
- E pollLast();
-
- /**
- * Retrieves, but does not remove, the first element of this deque.
- *
- * This method differs from {@link #peekFirst peekFirst} only in that it
- * throws an exception if this deque is empty.
- *
- * @return the head of this deque
- * @throws NoSuchElementException if this deque is empty
- */
- E getFirst();
-
- /**
- * Retrieves, but does not remove, the last element of this deque.
- * This method differs from {@link #peekLast peekLast} only in that it
- * throws an exception if this deque is empty.
- *
- * @return the tail of this deque
- * @throws NoSuchElementException if this deque is empty
- */
- E getLast();
-
- /**
- * Retrieves, but does not remove, the first element of this deque,
- * or returns <tt>null</tt> if this deque is empty.
- *
- * @return the head of this deque, or <tt>null</tt> if this deque is empty
- */
- E peekFirst();
-
- /**
- * Retrieves, but does not remove, the last element of this deque,
- * or returns <tt>null</tt> if this deque is empty.
- *
- * @return the tail of this deque, or <tt>null</tt> if this deque is empty
- */
- E peekLast();
-
- /**
- * Removes the first occurrence of the specified element from this deque.
- * If the deque does not contain the element, it is unchanged.
- * More formally, removes the first element <tt>e</tt> such that
- * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>
- * (if such an element exists).
- * Returns <tt>true</tt> if this deque contained the specified element
- * (or equivalently, if this deque changed as a result of the call).
- *
- * @param o element to be removed from this deque, if present
- * @return <tt>true</tt> if an element was removed as a result of this call
- * @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements (optional)
- */
- boolean removeFirstOccurrence(Object o);
-
- /**
- * Removes the last occurrence of the specified element from this deque.
- * If the deque does not contain the element, it is unchanged.
- * More formally, removes the last element <tt>e</tt> such that
- * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>
- * (if such an element exists).
- * Returns <tt>true</tt> if this deque contained the specified element
- * (or equivalently, if this deque changed as a result of the call).
- *
- * @param o element to be removed from this deque, if present
- * @return <tt>true</tt> if an element was removed as a result of this call
- * @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements (optional)
- */
- boolean removeLastOccurrence(Object o);
-
- // *** Queue methods ***
-
- /**
- * Inserts the specified element into the queue represented by this deque
- * (in other words, at the tail of this deque) if it is possible to do so
- * immediately without violating capacity restrictions, returning
- * <tt>true</tt> upon success and throwing an
- * <tt>IllegalStateException</tt> if no space is currently available.
- * When using a capacity-restricted deque, it is generally preferable to
- * use {@link #offer(Object) offer}.
- *
- * <p>This method is equivalent to {@link #addLast}.
- *
- * @param e the element to add
- * @return <tt>true</tt> (as specified by {@link Collection#add})
- * @throws IllegalStateException if the element cannot be added at this
- * time due to capacity restrictions
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- boolean add(E e);
-
- /**
- * Inserts the specified element into the queue represented by this deque
- * (in other words, at the tail of this deque) if it is possible to do so
- * immediately without violating capacity restrictions, returning
- * <tt>true</tt> upon success and <tt>false</tt> if no space is currently
- * available. When using a capacity-restricted deque, this method is
- * generally preferable to the {@link #add} method, which can fail to
- * insert an element only by throwing an exception.
- *
- * <p>This method is equivalent to {@link #offerLast}.
- *
- * @param e the element to add
- * @return <tt>true</tt> if the element was added to this deque, else
- * <tt>false</tt>
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- boolean offer(E e);
-
- /**
- * Retrieves and removes the head of the queue represented by this deque
- * (in other words, the first element of this deque).
- * This method differs from {@link #poll poll} only in that it throws an
- * exception if this deque is empty.
- *
- * <p>This method is equivalent to {@link #removeFirst()}.
- *
- * @return the head of the queue represented by this deque
- * @throws NoSuchElementException if this deque is empty
- */
- E remove();
-
- /**
- * Retrieves and removes the head of the queue represented by this deque
- * (in other words, the first element of this deque), or returns
- * <tt>null</tt> if this deque is empty.
- *
- * <p>This method is equivalent to {@link #pollFirst()}.
- *
- * @return the first element of this deque, or <tt>null</tt> if
- * this deque is empty
- */
- E poll();
-
- /**
- * Retrieves, but does not remove, the head of the queue represented by
- * this deque (in other words, the first element of this deque).
- * This method differs from {@link #peek peek} only in that it throws an
- * exception if this deque is empty.
- *
- * <p>This method is equivalent to {@link #getFirst()}.
- *
- * @return the head of the queue represented by this deque
- * @throws NoSuchElementException if this deque is empty
- */
- E element();
-
- /**
- * Retrieves, but does not remove, the head of the queue represented by
- * this deque (in other words, the first element of this deque), or
- * returns <tt>null</tt> if this deque is empty.
- *
- * <p>This method is equivalent to {@link #peekFirst()}.
- *
- * @return the head of the queue represented by this deque, or
- * <tt>null</tt> if this deque is empty
- */
- E peek();
-
- // *** Stack methods ***
-
- /**
- * Pushes an element onto the stack represented by this deque (in other
- * words, at the head of this deque) if it is possible to do so
- * immediately without violating capacity restrictions, returning
- * <tt>true</tt> upon success and throwing an
- * <tt>IllegalStateException</tt> if no space is currently available.
- *
- * <p>This method is equivalent to {@link #addFirst}.
- *
- * @param e the element to push
- * @throws IllegalStateException if the element cannot be added at this
- * time due to capacity restrictions
- * @throws ClassCastException if the class of the specified element
- * prevents it from being added to this deque
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements
- * @throws IllegalArgumentException if some property of the specified
- * element prevents it from being added to this deque
- */
- void push(E e);
-
- /**
- * Pops an element from the stack represented by this deque. In other
- * words, removes and returns the first element of this deque.
- *
- * <p>This method is equivalent to {@link #removeFirst()}.
- *
- * @return the element at the front of this deque (which is the top
- * of the stack represented by this deque)
- * @throws NoSuchElementException if this deque is empty
- */
- E pop();
-
- // *** Collection methods ***
-
- /**
- * Removes the first occurrence of the specified element from this deque.
- * If the deque does not contain the element, it is unchanged.
- * More formally, removes the first element <tt>e</tt> such that
- * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>
- * (if such an element exists).
- * Returns <tt>true</tt> if this deque contained the specified element
- * (or equivalently, if this deque changed as a result of the call).
- *
- * <p>This method is equivalent to {@link #removeFirstOccurrence}.
- *
- * @param o element to be removed from this deque, if present
- * @return <tt>true</tt> if an element was removed as a result of this call
- * @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements (optional)
- */
- boolean remove(Object o);
-
- /**
- * Returns <tt>true</tt> if this deque contains the specified element.
- * More formally, returns <tt>true</tt> if and only if this deque contains
- * at least one element <tt>e</tt> such that
- * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
- *
- * @param o element whose presence in this deque is to be tested
- * @return <tt>true</tt> if this deque contains the specified element
- * @throws ClassCastException if the type of the specified element
- * is incompatible with this deque (optional)
- * @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements (optional)
- */
- boolean contains(Object o);
-
- /**
- * Returns the number of elements in this deque.
- *
- * @return the number of elements in this deque
- */
- public int size();
-
- /**
- * Returns an iterator over the elements in this deque in proper sequence.
- * The elements will be returned in order from first (head) to last (tail).
- *
- * @return an iterator over the elements in this deque in proper sequence
- */
- Iterator<E> iterator();
-
- /**
- * Returns an iterator over the elements in this deque in reverse
- * sequential order. The elements will be returned in order from
- * last (tail) to first (head).
- *
- * @return an iterator over the elements in this deque in reverse
- * sequence
- */
- Iterator<E> descendingIterator();
+ /**
+ * Inserts the specified element at the front of this deque if it is
+ * possible to do so immediately without violating capacity restrictions.
+ * When using a capacity-restricted deque, it is generally preferable to
+ * use method {@link #offerFirst}.
+ *
+ * @param e the element to add
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ void addFirst(E e);
+
+ /**
+ * Inserts the specified element at the end of this deque if it is
+ * possible to do so immediately without violating capacity restrictions.
+ * When using a capacity-restricted deque, it is generally preferable to
+ * use method {@link #offerLast}.
+ *
+ * <p>This method is equivalent to {@link #add}.
+ *
+ * @param e the element to add
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ void addLast(E e);
+
+ /**
+ * Inserts the specified element at the front of this deque unless it would
+ * violate capacity restrictions. When using a capacity-restricted deque,
+ * this method is generally preferable to the {@link #addFirst} method,
+ * which can fail to insert an element only by throwing an exception.
+ *
+ * @param e the element to add
+ * @return <tt>true</tt> if the element was added to this deque, else
+ * <tt>false</tt>
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ boolean offerFirst(E e);
+
+ /**
+ * Inserts the specified element at the end of this deque unless it would
+ * violate capacity restrictions. When using a capacity-restricted deque,
+ * this method is generally preferable to the {@link #addLast} method,
+ * which can fail to insert an element only by throwing an exception.
+ *
+ * @param e the element to add
+ * @return <tt>true</tt> if the element was added to this deque, else
+ * <tt>false</tt>
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ boolean offerLast(E e);
+
+ /**
+ * Retrieves and removes the first element of this deque. This method
+ * differs from {@link #pollFirst pollFirst} only in that it throws an
+ * exception if this deque is empty.
+ *
+ * @return the head of this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E removeFirst();
+
+ /**
+ * Retrieves and removes the last element of this deque. This method
+ * differs from {@link #pollLast pollLast} only in that it throws an
+ * exception if this deque is empty.
+ *
+ * @return the tail of this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E removeLast();
+
+ /**
+ * Retrieves and removes the first element of this deque,
+ * or returns <tt>null</tt> if this deque is empty.
+ *
+ * @return the head of this deque, or <tt>null</tt> if this deque is empty
+ */
+ E pollFirst();
+
+ /**
+ * Retrieves and removes the last element of this deque,
+ * or returns <tt>null</tt> if this deque is empty.
+ *
+ * @return the tail of this deque, or <tt>null</tt> if this deque is empty
+ */
+ E pollLast();
+
+ /**
+ * Retrieves, but does not remove, the first element of this deque.
+ *
+ * This method differs from {@link #peekFirst peekFirst} only in that it
+ * throws an exception if this deque is empty.
+ *
+ * @return the head of this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E getFirst();
+
+ /**
+ * Retrieves, but does not remove, the last element of this deque.
+ * This method differs from {@link #peekLast peekLast} only in that it
+ * throws an exception if this deque is empty.
+ *
+ * @return the tail of this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E getLast();
+
+ /**
+ * Retrieves, but does not remove, the first element of this deque,
+ * or returns <tt>null</tt> if this deque is empty.
+ *
+ * @return the head of this deque, or <tt>null</tt> if this deque is empty
+ */
+ E peekFirst();
+
+ /**
+ * Retrieves, but does not remove, the last element of this deque,
+ * or returns <tt>null</tt> if this deque is empty.
+ *
+ * @return the tail of this deque, or <tt>null</tt> if this deque is empty
+ */
+ E peekLast();
+
+ /**
+ * Removes the first occurrence of the specified element from this deque.
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the first element <tt>e</tt> such that
+ * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>
+ * (if such an element exists).
+ * Returns <tt>true</tt> if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
+ *
+ * @param o element to be removed from this deque, if present
+ * @return <tt>true</tt> if an element was removed as a result of this call
+ * @throws ClassCastException if the class of the specified element
+ * is incompatible with this deque (optional)
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements (optional)
+ */
+ boolean removeFirstOccurrence(Object o);
+
+ /**
+ * Removes the last occurrence of the specified element from this deque.
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the last element <tt>e</tt> such that
+ * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>
+ * (if such an element exists).
+ * Returns <tt>true</tt> if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
+ *
+ * @param o element to be removed from this deque, if present
+ * @return <tt>true</tt> if an element was removed as a result of this call
+ * @throws ClassCastException if the class of the specified element
+ * is incompatible with this deque (optional)
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements (optional)
+ */
+ boolean removeLastOccurrence(Object o);
+
+ // *** Queue methods ***
+
+ /**
+ * Inserts the specified element into the queue represented by this deque
+ * (in other words, at the tail of this deque) if it is possible to do so
+ * immediately without violating capacity restrictions, returning
+ * <tt>true</tt> upon success and throwing an
+ * <tt>IllegalStateException</tt> if no space is currently available.
+ * When using a capacity-restricted deque, it is generally preferable to
+ * use {@link #offer(Object) offer}.
+ *
+ * <p>This method is equivalent to {@link #addLast}.
+ *
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Collection#add})
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ boolean add(E e);
+
+ /**
+ * Inserts the specified element into the queue represented by this deque
+ * (in other words, at the tail of this deque) if it is possible to do so
+ * immediately without violating capacity restrictions, returning
+ * <tt>true</tt> upon success and <tt>false</tt> if no space is currently
+ * available. When using a capacity-restricted deque, this method is
+ * generally preferable to the {@link #add} method, which can fail to
+ * insert an element only by throwing an exception.
+ *
+ * <p>This method is equivalent to {@link #offerLast}.
+ *
+ * @param e the element to add
+ * @return <tt>true</tt> if the element was added to this deque, else
+ * <tt>false</tt>
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ boolean offer(E e);
+
+ /**
+ * Retrieves and removes the head of the queue represented by this deque
+ * (in other words, the first element of this deque).
+ * This method differs from {@link #poll poll} only in that it throws an
+ * exception if this deque is empty.
+ *
+ * <p>This method is equivalent to {@link #removeFirst()}.
+ *
+ * @return the head of the queue represented by this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E remove();
+
+ /**
+ * Retrieves and removes the head of the queue represented by this deque
+ * (in other words, the first element of this deque), or returns
+ * <tt>null</tt> if this deque is empty.
+ *
+ * <p>This method is equivalent to {@link #pollFirst()}.
+ *
+ * @return the first element of this deque, or <tt>null</tt> if
+ * this deque is empty
+ */
+ E poll();
+
+ /**
+ * Retrieves, but does not remove, the head of the queue represented by
+ * this deque (in other words, the first element of this deque).
+ * This method differs from {@link #peek peek} only in that it throws an
+ * exception if this deque is empty.
+ *
+ * <p>This method is equivalent to {@link #getFirst()}.
+ *
+ * @return the head of the queue represented by this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E element();
+
+ /**
+ * Retrieves, but does not remove, the head of the queue represented by
+ * this deque (in other words, the first element of this deque), or
+ * returns <tt>null</tt> if this deque is empty.
+ *
+ * <p>This method is equivalent to {@link #peekFirst()}.
+ *
+ * @return the head of the queue represented by this deque, or
+ * <tt>null</tt> if this deque is empty
+ */
+ E peek();
+
+ // *** Stack methods ***
+
+ /**
+ * Pushes an element onto the stack represented by this deque (in other
+ * words, at the head of this deque) if it is possible to do so
+ * immediately without violating capacity restrictions, returning
+ * <tt>true</tt> upon success and throwing an
+ * <tt>IllegalStateException</tt> if no space is currently available.
+ *
+ * <p>This method is equivalent to {@link #addFirst}.
+ *
+ * @param e the element to push
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ void push(E e);
+
+ /**
+ * Pops an element from the stack represented by this deque. In other
+ * words, removes and returns the first element of this deque.
+ *
+ * <p>This method is equivalent to {@link #removeFirst()}.
+ *
+ * @return the element at the front of this deque (which is the top
+ * of the stack represented by this deque)
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E pop();
+
+ // *** Collection methods ***
+
+ /**
+ * Removes the first occurrence of the specified element from this deque.
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the first element <tt>e</tt> such that
+ * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>
+ * (if such an element exists).
+ * Returns <tt>true</tt> if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
+ *
+ * <p>This method is equivalent to {@link #removeFirstOccurrence}.
+ *
+ * @param o element to be removed from this deque, if present
+ * @return <tt>true</tt> if an element was removed as a result of this call
+ * @throws ClassCastException if the class of the specified element
+ * is incompatible with this deque (optional)
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements (optional)
+ */
+ boolean remove(Object o);
+
+ /**
+ * Returns <tt>true</tt> if this deque contains the specified element.
+ * More formally, returns <tt>true</tt> if and only if this deque contains
+ * at least one element <tt>e</tt> such that
+ * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
+ *
+ * @param o element whose presence in this deque is to be tested
+ * @return <tt>true</tt> if this deque contains the specified element
+ * @throws ClassCastException if the type of the specified element
+ * is incompatible with this deque (optional)
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements (optional)
+ */
+ boolean contains(Object o);
+
+ /**
+ * Returns the number of elements in this deque.
+ *
+ * @return the number of elements in this deque
+ */
+ public int size();
+
+ /**
+ * Returns an iterator over the elements in this deque in proper sequence.
+ * The elements will be returned in order from first (head) to last (tail).
+ *
+ * @return an iterator over the elements in this deque in proper sequence
+ */
+ Iterator<E> iterator();
+
+ /**
+ * Returns an iterator over the elements in this deque in reverse
+ * sequential order. The elements will be returned in order from
+ * last (tail) to first (head).
+ *
+ * @return an iterator over the elements in this deque in reverse
+ * sequence
+ */
+ Iterator<E> descendingIterator();
}
diff --git a/platform/android/viewer/src/com/artifex/mupdfdemo/MuPDFActivity.java b/platform/android/viewer/src/com/artifex/mupdfdemo/MuPDFActivity.java
index 91b1e8fe..08d32be9 100644
--- a/platform/android/viewer/src/com/artifex/mupdfdemo/MuPDFActivity.java
+++ b/platform/android/viewer/src/com/artifex/mupdfdemo/MuPDFActivity.java
@@ -274,7 +274,7 @@ public class MuPDFActivity extends Activity implements FilePicker.FilePickerSupp
super.onCreate(savedInstanceState);
mAlertBuilder = new AlertDialog.Builder(this);
- gAlertBuilder = mAlertBuilder; // keep a static copy of this that other classes can use
+ gAlertBuilder = mAlertBuilder; // keep a static copy of this that other classes can use
if (core == null) {
core = (MuPDFCore)getLastNonConfigurationInstance();
diff --git a/platform/android/viewer/src/com/artifex/mupdfdemo/OutlineActivityData.java b/platform/android/viewer/src/com/artifex/mupdfdemo/OutlineActivityData.java
index a703e61e..be1386e6 100644
--- a/platform/android/viewer/src/com/artifex/mupdfdemo/OutlineActivityData.java
+++ b/platform/android/viewer/src/com/artifex/mupdfdemo/OutlineActivityData.java
@@ -2,7 +2,7 @@ package com.artifex.mupdfdemo;
public class OutlineActivityData {
public OutlineItem items[];
- public int position;
+ public int position;
static private OutlineActivityData singleton;
static public void set(OutlineActivityData d) {
diff --git a/platform/android/viewer/src/com/artifex/mupdfdemo/OutlineAdapter.java b/platform/android/viewer/src/com/artifex/mupdfdemo/OutlineAdapter.java
index 4251ed8e..c1b22021 100644
--- a/platform/android/viewer/src/com/artifex/mupdfdemo/OutlineAdapter.java
+++ b/platform/android/viewer/src/com/artifex/mupdfdemo/OutlineAdapter.java
@@ -7,11 +7,11 @@ import android.widget.BaseAdapter;
import android.widget.TextView;
public class OutlineAdapter extends BaseAdapter {
- private final OutlineItem mItems[];
+ private final OutlineItem mItems[];
private final LayoutInflater mInflater;
public OutlineAdapter(LayoutInflater inflater, OutlineItem items[]) {
mInflater = inflater;
- mItems = items;
+ mItems = items;
}
public int getCount() {
diff --git a/platform/android/viewer/src/com/artifex/mupdfdemo/OutlineItem.java b/platform/android/viewer/src/com/artifex/mupdfdemo/OutlineItem.java
index 7730991e..0ed041c2 100644
--- a/platform/android/viewer/src/com/artifex/mupdfdemo/OutlineItem.java
+++ b/platform/android/viewer/src/com/artifex/mupdfdemo/OutlineItem.java
@@ -1,14 +1,14 @@
package com.artifex.mupdfdemo;
public class OutlineItem {
- public final int level;
+ public final int level;
public final String title;
- public final int page;
+ public final int page;
OutlineItem(int _level, String _title, int _page) {
level = _level;
title = _title;
- page = _page;
+ page = _page;
}
}
diff --git a/platform/android/viewer/src/com/artifex/mupdfdemo/PageView.java b/platform/android/viewer/src/com/artifex/mupdfdemo/PageView.java
index c897f414..502e0c2b 100644
--- a/platform/android/viewer/src/com/artifex/mupdfdemo/PageView.java
+++ b/platform/android/viewer/src/com/artifex/mupdfdemo/PageView.java
@@ -94,7 +94,7 @@ public abstract class PageView extends ViewGroup {
private static final float INK_THICKNESS = 10.0f;
private static final int BACKGROUND_COLOR = 0xFFFFFFFF;
private static final int PROGRESS_DIALOG_DELAY = 200;
- protected final Context mContext;
+ protected final Context mContext;
protected int mPageNumber;
private Point mParentSize;
protected Point mSize; // Size of page at minimum zoom
@@ -127,7 +127,7 @@ public abstract class PageView extends ViewGroup {
public PageView(Context c, Point parentSize, Bitmap sharedHqBm) {
super(c);
- mContext = c;
+ mContext = c;
mParentSize = parentSize;
setBackgroundColor(BACKGROUND_COLOR);
mEntireBm = Bitmap.createBitmap(parentSize.x, parentSize.y, Config.ARGB_8888);
@@ -201,7 +201,7 @@ public abstract class PageView extends ViewGroup {
public void releaseBitmaps() {
reinit();
- // recycle bitmaps before releasing them.
+ // recycle bitmaps before releasing them.
if (mEntireBm!=null)
mEntireBm.recycle();
@@ -320,16 +320,16 @@ public abstract class PageView extends ViewGroup {
paint.setColor(HIGHLIGHT_COLOR);
for (RectF rect : mSearchBoxes)
canvas.drawRect(rect.left*scale, rect.top*scale,
- rect.right*scale, rect.bottom*scale,
- paint);
+ rect.right*scale, rect.bottom*scale,
+ paint);
}
if (!mIsBlank && mLinks != null && mHighlightLinks) {
paint.setColor(LINK_COLOR);
for (LinkInfo link : mLinks)
canvas.drawRect(link.rect.left*scale, link.rect.top*scale,
- link.rect.right*scale, link.rect.bottom*scale,
- paint);
+ link.rect.right*scale, link.rect.bottom*scale,
+ paint);
}
if (mSelectBox != null && mText != null) {
@@ -536,7 +536,7 @@ public abstract class PageView extends ViewGroup {
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- int w = right-left;
+ int w = right-left;
int h = bottom-top;
if (mEntire != null) {
@@ -556,7 +556,7 @@ public abstract class PageView extends ViewGroup {
if (mPatchViewSize.x != w || mPatchViewSize.y != h) {
// Zoomed since patch was created
mPatchViewSize = null;
- mPatchArea = null;
+ mPatchArea = null;
if (mPatch != null) {
mPatch.setImageBitmap(null);
mPatch.invalidate();
@@ -630,7 +630,7 @@ public abstract class PageView extends ViewGroup {
public void onPostExecute(Void result) {
mPatchViewSize = patchViewSize;
- mPatchArea = patchArea;
+ mPatchArea = patchArea;
mPatch.setImageBitmap(mPatchBm);
mPatch.invalidate();
//requestLayout();
diff --git a/platform/android/viewer/src/com/artifex/mupdfdemo/ReaderView.java b/platform/android/viewer/src/com/artifex/mupdfdemo/ReaderView.java
index f91efa41..bfccdb19 100644
--- a/platform/android/viewer/src/com/artifex/mupdfdemo/ReaderView.java
+++ b/platform/android/viewer/src/com/artifex/mupdfdemo/ReaderView.java
@@ -24,14 +24,14 @@ import android.widget.Scroller;
public class ReaderView
extends AdapterView<Adapter>
implements GestureDetector.OnGestureListener, ScaleGestureDetector.OnScaleGestureListener, Runnable {
- private static final int MOVING_DIAGONALLY = 0;
- private static final int MOVING_LEFT = 1;
- private static final int MOVING_RIGHT = 2;
- private static final int MOVING_UP = 3;
- private static final int MOVING_DOWN = 4;
+ private static final int MOVING_DIAGONALLY = 0;
+ private static final int MOVING_LEFT = 1;
+ private static final int MOVING_RIGHT = 2;
+ private static final int MOVING_UP = 3;
+ private static final int MOVING_DOWN = 4;
- private static final int FLING_MARGIN = 100;
- private static final int GAP = 20;
+ private static final int FLING_MARGIN = 100;
+ private static final int GAP = 20;
private static final float MIN_SCALE = 1.0f;
private static final float MAX_SCALE = 5.0f;
@@ -74,7 +74,7 @@ public class ReaderView
super(context);
mGestureDetector = new GestureDetector(this);
mScaleGestureDetector = new ScaleGestureDetector(context, this);
- mScroller = new Scroller(context);
+ mScroller = new Scroller(context);
mStepper = new Stepper(this, this);
}
@@ -94,7 +94,7 @@ public class ReaderView
{
mGestureDetector = new GestureDetector(this);
mScaleGestureDetector = new ScaleGestureDetector(context, this);
- mScroller = new Scroller(context);
+ mScroller = new Scroller(context);
mStepper = new Stepper(this, this);
}
}
@@ -103,7 +103,7 @@ public class ReaderView
super(context, attrs, defStyle);
mGestureDetector = new GestureDetector(this);
mScaleGestureDetector = new ScaleGestureDetector(context, this);
- mScroller = new Scroller(context);
+ mScroller = new Scroller(context);
mStepper = new Stepper(this, this);
}
@@ -173,7 +173,7 @@ public class ReaderView
// code.
// screenWidth/Height are the actual width/height of the screen. e.g. 480/800
- int screenWidth = getWidth();
+ int screenWidth = getWidth();
int screenHeight = getHeight();
// We might be mid scroll; we want to calculate where we scroll to based on
// where this scroll would end, not where we are now (to allow for people
@@ -181,11 +181,11 @@ public class ReaderView
int remainingX = mScroller.getFinalX() - mScroller.getCurrX();
int remainingY = mScroller.getFinalY() - mScroller.getCurrY();
// right/bottom is in terms of pixels within the scaled document; e.g. 1000
- int top = -(v.getTop() + mYScroll + remainingY);
- int right = screenWidth -(v.getLeft() + mXScroll + remainingX);
+ int top = -(v.getTop() + mYScroll + remainingY);
+ int right = screenWidth -(v.getLeft() + mXScroll + remainingX);
int bottom = screenHeight+top;
// docWidth/Height are the width/height of the scaled document e.g. 2000x3000
- int docWidth = v.getMeasuredWidth();
+ int docWidth = v.getMeasuredWidth();
int docHeight = v.getMeasuredHeight();
int xOffset, yOffset;
@@ -196,7 +196,7 @@ public class ReaderView
View nv = mChildViews.get(mCurrent+1);
if (nv == null) // No page to advance to
return;
- int nextTop = -(nv.getTop() + mYScroll + remainingY);
+ int nextTop = -(nv.getTop() + mYScroll + remainingY);
int nextLeft = -(nv.getLeft() + mXScroll + remainingX);
int nextDocWidth = nv.getMeasuredWidth();
int nextDocHeight = nv.getMeasuredHeight();
@@ -246,7 +246,7 @@ public class ReaderView
// code.
// screenWidth/Height are the actual width/height of the screen. e.g. 480/800
- int screenWidth = getWidth();
+ int screenWidth = getWidth();
int screenHeight = getHeight();
// We might be mid scroll; we want to calculate where we scroll to based on
// where this scroll would end, not where we are now (to allow for people
@@ -254,8 +254,8 @@ public class ReaderView
int remainingX = mScroller.getFinalX() - mScroller.getCurrX();
int remainingY = mScroller.getFinalY() - mScroller.getCurrY();
// left/top is in terms of pixels within the scaled document; e.g. 1000
- int left = -(v.getLeft() + mXScroll + remainingX);
- int top = -(v.getTop() + mYScroll + remainingY);
+ int left = -(v.getLeft() + mXScroll + remainingX);
+ int top = -(v.getTop() + mYScroll + remainingY);
// docWidth/Height are the width/height of the scaled document e.g. 2000x3000
int docHeight = v.getMeasuredHeight();
@@ -273,8 +273,8 @@ public class ReaderView
// Allow for the next page maybe being shorter than the screen is high
yOffset = (prevDocHeight < screenHeight ? ((prevDocHeight - screenHeight)>>1) : 0);
- int prevLeft = -(pv.getLeft() + mXScroll);
- int prevTop = -(pv.getTop() + mYScroll);
+ int prevLeft = -(pv.getLeft() + mXScroll);
+ int prevTop = -(pv.getTop() + mYScroll);
if (prevDocWidth < screenWidth) {
// Previous page is too narrow to fill the screen. Scroll to the bottom, centred.
xOffset = (prevDocWidth - screenWidth)>>1;
@@ -580,9 +580,9 @@ public class ReaderView
catch (java.lang.OutOfMemoryError e) {
System.out.println("Out of memory during layout");
- // we might get an out of memory error.
- // so let's display an alert.
- // TODO: a better message, in resources.
+ // we might get an out of memory error.
+ // so let's display an alert.
+ // TODO: a better message, in resources.
if (!memAlert) {
memAlert = true;
@@ -700,37 +700,37 @@ public class ReaderView
// the views spaced out
cvOffset = subScreenSizeOffset(cv);
if (notPresent) {
- //Main item not already present. Just place it top left
+ // Main item not already present. Just place it top left
cvLeft = cvOffset.x;
- cvTop = cvOffset.y;
+ cvTop = cvOffset.y;
} else {
// Main item already present. Adjust by scroll offsets
cvLeft = cv.getLeft() + mXScroll;
- cvTop = cv.getTop() + mYScroll;
+ cvTop = cv.getTop() + mYScroll;
}
// Scroll values have been accounted for
mXScroll = mYScroll = 0;
- cvRight = cvLeft + cv.getMeasuredWidth();
- cvBottom = cvTop + cv.getMeasuredHeight();
+ cvRight = cvLeft + cv.getMeasuredWidth();
+ cvBottom = cvTop + cv.getMeasuredHeight();
if (!mUserInteracting && mScroller.isFinished()) {
Point corr = getCorrection(getScrollBounds(cvLeft, cvTop, cvRight, cvBottom));
- cvRight += corr.x;
- cvLeft += corr.x;
- cvTop += corr.y;
+ cvRight += corr.x;
+ cvLeft += corr.x;
+ cvTop += corr.y;
cvBottom += corr.y;
} else if (HORIZONTAL_SCROLLING && cv.getMeasuredHeight() <= getHeight()) {
// When the current view is as small as the screen in height, clamp
// it vertically
Point corr = getCorrection(getScrollBounds(cvLeft, cvTop, cvRight, cvBottom));
- cvTop += corr.y;
+ cvTop += corr.y;
cvBottom += corr.y;
} else if (!HORIZONTAL_SCROLLING && cv.getMeasuredWidth() <= getWidth()) {
// When the current view is as small as the screen in width, clamp
// it horizontally
Point corr = getCorrection(getScrollBounds(cvLeft, cvTop, cvRight, cvBottom));
- cvRight += corr.x;
- cvLeft += corr.x;
+ cvRight += corr.x;
+ cvLeft += corr.x;
}
cv.layout(cvLeft, cvTop, cvRight, cvBottom);
@@ -871,14 +871,14 @@ public class ReaderView
// onLayout, so add mXScroll and mYScroll to the current
// positions when calculating the bounds.
return getScrollBounds(v.getLeft() + mXScroll,
- v.getTop() + mYScroll,
- v.getLeft() + v.getMeasuredWidth() + mXScroll,
- v.getTop() + v.getMeasuredHeight() + mYScroll);
+ v.getTop() + mYScroll,
+ v.getLeft() + v.getMeasuredWidth() + mXScroll,
+ v.getTop() + v.getMeasuredHeight() + mYScroll);
}
private Point getCorrection(Rect bounds) {
return new Point(Math.min(Math.max(0,bounds.left),bounds.right),
- Math.min(Math.max(0,bounds.top),bounds.bottom));
+ Math.min(Math.max(0,bounds.top),bounds.bottom));
}
private void postSettle(final View v) {
@@ -926,10 +926,10 @@ public class ReaderView
private static boolean withinBoundsInDirectionOfTravel(Rect bounds, float vx, float vy) {
switch (directionOfTravel(vx, vy)) {
case MOVING_DIAGONALLY: return bounds.contains(0, 0);
- case MOVING_LEFT: return bounds.left <= 0;
- case MOVING_RIGHT: return bounds.right >= 0;
- case MOVING_UP: return bounds.top <= 0;
- case MOVING_DOWN: return bounds.bottom >= 0;
+ case MOVING_LEFT: return bounds.left <= 0;
+ case MOVING_RIGHT: return bounds.right >= 0;
+ case MOVING_UP: return bounds.top <= 0;
+ case MOVING_DOWN: return bounds.bottom >= 0;
default: throw new NoSuchElementException();
}
}
diff --git a/platform/android/viewer/src/com/artifex/mupdfdemo/SearchTask.java b/platform/android/viewer/src/com/artifex/mupdfdemo/SearchTask.java
index d3969f10..7e15f98d 100644
--- a/platform/android/viewer/src/com/artifex/mupdfdemo/SearchTask.java
+++ b/platform/android/viewer/src/com/artifex/mupdfdemo/SearchTask.java
@@ -88,7 +88,7 @@ public abstract class SearchTask {
protected void onPostExecute(SearchTaskResult result) {
progressDialog.cancel();
if (result != null) {
- onTextFound(result);
+ onTextFound(result);
} else {
mAlertBuilder.setTitle(SearchTaskResult.get() == null ? R.string.text_not_found : R.string.no_further_occurrences_found);
AlertDialog alert = mAlertBuilder.create();
diff --git a/platform/android/viewer/src/com/artifex/mupdfdemo/SearchTaskResult.java b/platform/android/viewer/src/com/artifex/mupdfdemo/SearchTaskResult.java
index 8fa3c3a2..6c337fe2 100644
--- a/platform/android/viewer/src/com/artifex/mupdfdemo/SearchTaskResult.java
+++ b/platform/android/viewer/src/com/artifex/mupdfdemo/SearchTaskResult.java
@@ -4,7 +4,7 @@ import android.graphics.RectF;
public class SearchTaskResult {
public final String txt;
- public final int pageNumber;
+ public final int pageNumber;
public final RectF searchBoxes[];
static private SearchTaskResult singleton;